skip to content

What does the Angular CLI's refactor-jasmine-vitest schematic rewrite in spec files, and what must you still check or fix by hand?

level: middleimportance: nice to knowfreq 18%

answer

  1. spec files, not configuration
  2. focus, skip and spy calls
  3. globals mean no imports
  4. TODO comments mark the gaps

basics

~20 s

refactor-jasmine-vitest rewrites Jasmine APIs in spec files to Vitest (fit to it.only, spyOn to vi.spyOn, createSpy to vi.fn and more) and leaves TODOs; it installs nothing, changes no angular.json, and cannot handle every complex spy.

solid answer

~30 s

It is a schematic in `@schematics/angular`, run as `ng g @schematics/angular:refactor-jasmine-vitest` once the project is configured for Vitest. It rewrites Jasmine-specific code: `fit`/`fdescribe` to `.only`, `xit`/`xdescribe` to `.skip`, `spyOn` to `vi.spyOn`, `jasmine.createSpy` to `vi.fn`, `jasmine.objectContaining` and `jasmine.any` to their `expect` forms, `fail` to `vi.fail`, plus matcher and hook adjustments, and writes a report. Options include `--include` for slicing a large suite, `--add-imports` (off because the builder enables Vitest globals), `--browser-mode` and `--fake-async`. It does not install packages, edit `angular.json` or delete Karma files, and complex spies get TODO comments, so I review every change and compare executed-test counts.

go deeper

for a junior

Recall that the schematic rewrites Jasmine APIs in spec files, such as spyOn to vi.spyOn and fit to it.only, and nothing else.

for a middle

Explain its main transformations and options, why imports are off by default, and the list of things it leaves undone.

for a senior

Run it in reviewable slices, resolve its TODOs, and verify executed-test counts and spy semantics rather than trusting a green run.

for a principal

Plan the refactor across teams so large suites move incrementally, with review ownership and metrics that catch silently weakened tests.

## What the schematic is for Moving an Angular project from Karma to Vitest has two halves. The configuration half (`angular.json`, dependencies, tsconfig types) is handled by the optional `migrate-karma-to-vitest` update migration or by hand. The **spec files** are the other half: they are written against **Jasmine's** globals, and the Vitest runner does not provide Jasmine's spy API. The Angular CLI's **`refactor-jasmine-vitest`** schematic in `@schematics/angular` rewrites those files. The Angular docs label it experimental and ask you to review every change. Run it with `ng g @schematics/angular:refactor-jasmine-vitest`, once the project is already configured for Vitest. ## What it rewrites | Jasmine form | Rewritten to | |---|---| | `fit`, `fdescribe` | `it.only`, `describe.only` | | `xit`, `xdescribe` | `it.skip`, `describe.skip` | | `spyOn(obj, 'm')` | `vi.spyOn(obj, 'm')` | | `jasmine.createSpy()` | `vi.fn()` | | `jasmine.objectContaining(...)` | `expect.objectContaining(...)` | | `jasmine.any(Type)` | `expect.any(Type)` | | `fail(...)` | `vi.fail(...)` | Beyond the table it also adjusts expectation matchers to their Vitest equivalents, updates the setup and teardown hooks, and adds **TODO comments** where it cannot convert something automatically. By default it writes a summary report, `jasmine-vitest-<date>.md`, in the project root. ## Options worth knowing - `--project` picks the project in a multi-project workspace. - `--include` limits the run to one file or directory, which is how a large suite is refactored in reviewable slices. - `--file-suffix` handles suites named `.test.ts` instead of `.spec.ts`. - `--add-imports` adds explicit `vitest` imports. It defaults to `false` because the unit-test builder enables Vitest's **globals**, so `describe`, `it`, `expect` and `vi` need no import. - `--browser-mode` leaves `toHaveClass` assertions alone, because Vitest's browser mode provides that matcher; otherwise they are rewritten to an equivalent. - `--fake-async` converts `fakeAsync` tests to Vitest fake timers instead of leaving them on Zone.js. - `--verbose` logs every transformation. ## What it does not do 1. It does **not** install `vitest`, `jsdom` or any other dependency. 2. It does **not** change `angular.json` or move build options off the test target. 3. It does **not** delete `karma.conf.js` or `src/test.ts`. 4. It does **not** handle every complex or nested spy scenario; those need manual rewriting. 5. It does **not** prove semantic equivalence. Jasmine and Vitest spies differ in details, so passing tests after the rewrite still need a reviewer's eye. ## A worked slice For a large suite, one feature folder at a time keeps each change reviewable: 1. Run `ng g @schematics/angular:refactor-jasmine-vitest --include=src/app/profile --verbose`. 2. Read the generated `jasmine-vitest-<date>.md` report for that run. 3. Resolve every TODO the schematic left in the folder. 4. Run only those specs, for example with the unit-test builder's `include` or `filter` options, and compare the executed count with the Karma baseline for the folder. 5. Open a pull request for that folder alone, then move to the next one. ## Reviewing the output - **Search for the TODOs** it added and resolve each one; they mark code it did not convert. - **Check spy behaviour**: a bare Jasmine spy does not call through to the original method, and the conversion tries to keep that stub-by-default meaning. Confirm that tests which relied on call-through still exercise the real code. - **Look for accidental focus or skips**: a leftover `.only` makes the rest of a file silently not run. - **Compare counts** of executed tests before and after, not only the pass rate. - **Refactor in slices** with `--include`, so each pull request is small enough to review. The Angular testing APIs in those files (`TestBed`, fixtures, `HttpTestingController`, harnesses) are left as they are, because they do not depend on the runner.

  • Why does the schematic not add import { describe, it, expect } from 'vitest' by default?
    The Angular unit-test builder turns on Vitest's globals option, so `describe`, `it`, `expect` and `vi` are available without imports, and the migration adds `vitest/globals` to the spec tsconfig types. `--add-imports` exists for projects that disabled globals in a custom Vitest configuration.
  • After the refactor the suite is green, but 40 fewer tests ran. What do you look for?
    Leftover focus and skips first: a converted `fit` or `fdescribe` becomes `.only` and silently excludes the rest of its file. Then files the run did not include, for example a custom suffix the schematic or builder did not match, and TODO-marked tests someone commented out. Compare executed counts per file against the Karma baseline.

saying these in an interview costs you the question

  • The schematic also switches the angular.json builder to Vitest
  • It installs vitest and jsdom as part of the refactor
  • Every spy scenario is converted, so no review is needed
  • Vitest requires explicit imports, so --add-imports is mandatory
  • It rewrites TestBed calls into Vitest equivalents