vitest-auto-spy
v5.60.0
Published
Auto-generate fully-typed test spies from a class — across Vitest, Bun, node:test and Rstest, with Angular/NestJS/React/Vue/Svelte recipes. Drop-in replacement for jest-auto-spies and jasmine-auto-spies.
Maintainers
Keywords
Readme
vitest-auto-spy
Typed test spies generated from a class, for Vitest, Bun, node:test and Rstest.
Pass a class and get a spy of every method, with helpers that match each method's return type.
No hand-written vi.fn() per method, and the spy never drifts from the class. It is a drop-in
replacement for jest-auto-spies and
jasmine-auto-spies.
📚 Documentation · 🧭 Spec patterns · 📦 npm · 🐙 GitHub · 🔖 Changelog
🤖 AGENTS.md · 🔤 llms.txt · 📄 llms-full.txt — works with Claude Code, OpenAI Codex, GLM, Cursor, Copilot, Gemini CLI and the rest
What you get
- A spy of every method from a class, typed, with helpers that match each return type:
resolveWithfor aPromise,nextWithfor anObservable,mockReturnValuefor the rest. - Answers per argument list:
calledWith(1).mockReturnValue(...). - A spy from a type alone:
createAutoMock<T>()andmockDeep<T>(). - Angular
TestBedin one line:provideAutoSpy(ApiService), theninjectSpy(ApiService). - One API on every runner: Vitest, Bun,
node:testand Rstest; helpers for Angular, NestJS, React, Vue / Pinia and Svelte. - Checks that make silent tests fail: strict mode, Observable assertions, cleanup between tests, 57 ESLint rules.
- Zero runtime dependencies.
Contents
- Install
- Quick start
- Core API
- Availability: runners and frameworks
- Versions and peer dependencies
- How to mock: one recipe per kind of dependency
- Everything else: where to go next
Reading this on npmjs.com? npm shows this file only up to Everything else. The documentation site has everything, and the whole README is on GitHub.
Install
npm i -D vitest-auto-spyOn Vitest, including Angular's ng test builder, you can import the helpers in a spec right away.
If you spy on Observables, add import 'vitest-auto-spy/rxjs' once, usually in your setup file. For Bun, node:test, Rstest and Analog, see
Installation → Wiring it up.
The plural name, vitest-auto-spies, is an alias
that re-exports this package. Prefer the singular.
Quick start
UserService loads a user through ApiService. The spec replaces ApiService with a spy and tests
the real UserService:
import { expect, it } from 'vitest';
import { createSpyFromClass } from 'vitest-auto-spy';
interface User {
id: number;
name: string;
}
class ApiService {
async get(url: string): Promise<User> {
const response = await fetch(url);
return response.json();
}
}
class UserService {
constructor(private readonly api: ApiService) {}
load(id: number): Promise<User> {
return this.api.get(`/users/${id}`);
}
}
it('loads the user from the API', async () => {
const api = createSpyFromClass(ApiService); // every method is a spy; the constructor never runs
api.get.resolveWith({ id: 1, name: 'Ada' }); // get() returns a Promise, so it has resolveWith
const user = await new UserService(api).load(1);
expect(user.name).toBe('Ada');
expect(api.get).toHaveBeenCalledWith('/users/1');
});An Angular component spec. CheckoutComponent injects CartService and shows its total:
import { TestBed } from '@angular/core/testing';
import { expect, it } from 'vitest';
import { injectSpy, provideAutoSpy, stable } from 'vitest-auto-spy/angular';
import { CartService } from './cart.service';
import { CheckoutComponent } from './checkout.component';
it('shows the cart total', async () => {
TestBed.configureTestingModule({
imports: [CheckoutComponent],
providers: [provideAutoSpy(CartService)], // CartService is now a spy in TestBed
});
const cart = injectSpy(CartService); // typed Spy<CartService>, no cast
cart.total.mockReturnValue(42); // configure before the component is created
const fixture = TestBed.createComponent(CheckoutComponent);
await stable(fixture); // runs change detection and effects, then waits
expect(fixture.nativeElement.textContent).toContain('42');
});Run it with npx vitest, or ng test on the Angular CLI. Step by step:
Getting started.
Core API
createSpyFromClass
Creates a spy of every method of a class. Use it for any dependency that is a class.
import { createSpyFromClass } from 'vitest-auto-spy';
const cart = createSpyFromClass(CartService);
cart.total.mockReturnValue(42);
// or set answers up front, where the spy is built
const seeded = createSpyFromClass(CartService, { returns: { total: 42 } });| Second argument | What it does |
| ------------------------------------ | ------------------------------------------------------------------------ |
| ['reload'] | an array: extra names to spy, besides the methods found on the prototype |
| { onlyMethodsToSpyOn } | spies only these methods |
| { instanceMethodsToSpyOn } | adds callables set in the constructor (signal(), arrow fields) |
| { observablePropsToSpyOn } | Observable properties to spy (needs vitest-auto-spy/rxjs) |
| { gettersToSpyOn, settersToSpyOn } | accessors to spy, under spy.accessorSpies |
| { returns } | a return value per method, set up front |
| { strict: true } | an unconfigured call throws |
Common mistake: an arrow-function field (load = () => {}) is not on the prototype, so it is
not spied. List it in instanceMethodsToSpyOn.
Every option: createSpyFromClass.
provideAutoSpy and injectSpy
provideAutoSpy registers a spy of a class in Angular's TestBed; injectSpy reads it back typed
as Spy<T>. NestJS has the same pair (vitest-auto-spy/nestjs); Vue has provideAutoSpy only.
import { injectSpy, provideAutoSpy } from 'vitest-auto-spy/angular';
TestBed.configureTestingModule({ providers: [provideAutoSpy(CartService)] });
const cart = injectSpy(CartService); // Spy<CartService>
cart.total.mockReturnValue(42);provideAutoSpy takes the same second argument as createSpyFromClass.
Common mistake: calling injectSpy(X) for a class you did not provideAutoSpy. You get the real
service; the library warns once and names the missing provider.
Full page: Angular.
calledWith and mustBeCalledWith
calledWith(...args) sets an answer for one argument list. mustBeCalledWith(...args) does the same
and throws on any other arguments.
users.getName.calledWith(1).mockReturnValue('Ada');
users.getName(1); // 'Ada'
users.getName(2); // undefined
users.getName.mustBeCalledWith(1).mockReturnValue('Ada');
users.getName(2); // throws: … Wanted: getName(1) Actual: getName(2)Matchers work too: calledWith(expect.any(Number)).
Common mistake: mockReturnValue and calledWith on the same method. The one set later replaces
the other; the library warns. For a fallback answer, use the returns option; calledWith answers
still win over it: createSpyFromClass(UserService, { returns: { getName: 'Unknown' } }).
Full page: Control helpers.
Return-type helpers
Each spied method gets helpers for what it returns:
| Method returns | Helpers |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| a value | mockReturnValue, calledWith(...), mustBeCalledWith(...), failWith(error) |
| a Promise | resolveWith(value), rejectWith(error), resolveWithPerCall([...]) |
| an Observable | nextWith(value) emits and keeps the stream open; nextOneTimeWith(value) emits once, then completes (like an HTTP call); nextWithValues([...]), throwWith(error), complete(), returnSubject() |
products.load.resolveWith([product]); // load(): Promise<Product[]>
products.items$.nextWith([product]); // items$(): Observable<Product[]>
payments.charge.calledWith(42).resolveWith({ ok: true }); // calledWith chains into every helperThe Observable helpers need one import per project, usually in the setup file (a spec works too):
import 'vitest-auto-spy/rxjs';.
Common mistake: nextWith is not a function. The vitest-auto-spy/rxjs import is missing.
Full page: Control helpers.
createAutoMock and mockDeep
createAutoMock<T>() creates a spy from a TypeScript interface or type, when there is no class at
run time. mockDeep<T>() does the same for nested objects.
import { createAutoMock, mockDeep } from 'vitest-auto-spy';
const gateway = createAutoMock<PaymentGateway>();
gateway.charge.resolveWith({ ok: true });
const api = mockDeep<ApiClient>();
api.users.find.resolveWith([user]); // nested members are spies tooCommon mistake: reading a data property you did not seed. With only a type, every unknown member
is a spy. Seed values: createAutoMock<Config>({ apiUrl: 'https://api.test' }).
Full page: Auto-mock by type.
strict
With strict: true, a call to a method you did not configure throws instead of returning
undefined. Use it so a test cannot pass on an empty answer.
const users = createSpyFromClass(UserService, { strict: true });
users.load.resolveWith([]);
users.currentTenant(); // throws: UserService.currentTenant() was called; this strict double has nothing configured for it.Turn it on for the whole suite with setupAutoSpy({ strict: true }) from vitest-auto-spy/setup.
Full page: Strict mode.
setupAutoSpy
setupAutoSpy() adds cleanup between tests to your setup file: it restores patched properties,
checks for two copies of the library, and can catch leaked timers, network calls and console output.
// vitest.setup.ts
import { setupAutoSpy } from 'vitest-auto-spy/setup';
setupAutoSpy(); // or setupAutoSpy({ preset: 'strict' }) for every guardEvery option: Test-run hygiene.
Availability
Every entry point is published. Pick the one for your runner, and add a framework entry if you use one. The API is the same everywhere.
| You use | Import from | Guide |
| ---------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------- |
| Vitest | vitest-auto-spy | Vitest |
| Bun (bun:test) | vitest-auto-spy/bun | Bun |
| node:test | vitest-auto-spy/node | node:test |
| Rstest | vitest-auto-spy/rstest | Rstest |
| RxJS Observables | vitest-auto-spy/rxjs (import once) | RxJS |
| Angular | vitest-auto-spy/angular | Angular |
| Angular HttpClient | vitest-auto-spy/angular-http | Angular HTTP |
| Angular router | vitest-auto-spy/angular-router | Angular router |
| Angular signal forms | vitest-auto-spy/signal-forms | Signal forms |
| Angular on Bun | vitest-auto-spy/bun-angular (a preload) | Angular on Bun |
| NestJS | vitest-auto-spy/nestjs | NestJS |
| React | vitest-auto-spy/react | React |
| Vue / Pinia | vitest-auto-spy/vue | Vue |
| Svelte | vitest-auto-spy/svelte | Svelte |
| a jasmine-auto-spies suite | vitest-auto-spy/jasmine | Migrating from jasmine-auto-spies |
Other entry points hold utilities: /setup, /console, /dom-stubs, /diagnostics, /zone,
/eslint-plugin and more. Full list: Installation → Entry points.
Versions and peer dependencies
Requirements
| Tool | Minimum |
| ---------- | ---------------------------------------------------------------- |
| Node.js | 22; CI runs 22, 24 and 26 |
| Vitest | 2.1; one install covers 2.1 through 5.x |
| Angular | 20 for the Angular entry points, 22 for /signal-forms |
| Bun | 1.4 for vitest-auto-spy/bun-angular; any recent Bun for /bun |
| TypeScript | 4.7 for the typed helpers; plain JavaScript works without types |
Vitest 5 needs no change on your side. Why each minimum is what it is: Compatibility.
The package ships ESM with bundled types. vitest-auto-spy/node and vitest-auto-spy/eslint-plugin
also ship CommonJS.
Peer dependencies
Every peer is optional: install one only if you use the entry point that needs it. The package has no runtime dependencies.
| Peer | Needed for | Version |
| --------------------------- | -------------------------------------------------------------------------------- | -------- |
| vitest | the default entry vitest-auto-spy and every Vitest-only entry | >=2.1 |
| rxjs | Observable spies from vitest-auto-spy/rxjs; rxjs 8 works too | >=7.2 |
| @angular/core | vitest-auto-spy/angular and vitest-auto-spy/bun-angular | >=20 |
| @angular/common | vitest-auto-spy/angular-http only | >=20 |
| @angular/router | vitest-auto-spy/angular-router only | >=20 |
| @angular/forms | vitest-auto-spy/signal-forms only; signal forms need Angular 22 | >=20 |
| @angular/platform-browser | By and the directive matchers, and the Bun preload | >=20 |
| @angular/compiler | vitest-auto-spy/angular/matchers and the vitest-auto-spy/bun-angular preload | >=20 |
| @rstest/core | vitest-auto-spy/rstest only | >=0.11 |
A suite on vitest-auto-spy/bun or vitest-auto-spy/node does not need Vitest installed, and its
types check without it.
How to mock
One short recipe per kind of dependency. Each links to the full page.
How to mock: a service behind Angular DI
import { injectSpy, provideAutoSpy } from 'vitest-auto-spy/angular';
TestBed.configureTestingModule({ providers: [provideAutoSpy(CartService)] });
const cart = injectSpy(CartService); // Spy<CartService>
cart.total.mockReturnValue(42);Because provideAutoSpy reads the real class, a method you add to CartService appears on the spy
too. A hand-written { provide: CartService, useValue: { total: vi.fn() } } stays out of date.
A primitive token is still fine as useValue: { provide: IS_BROWSER, useValue: false }. The lint
rules no-mistyped-use-value and no-unknown-use-value-key check such values and keys, which
Angular types as any.
On Vitest 4.1+, extendWithAutoSpies gives the spies as test fixtures:
import { of } from 'rxjs';
import { test as base } from 'vitest';
import { extendWithAutoSpies } from 'vitest-auto-spy/angular';
const test = extendWithAutoSpies(base, { cart: CartService, products: [ProductApi, { returns: { list: of([]) } }] });
test('checks out', ({ cart }) => cart.total.mockReturnValue(42));It throws needs Vitest 4.1 or newer on an older Vitest.
Details
How to mock: a service without DI
import { createAutoMock, createSpyFromClass } from 'vitest-auto-spy';
const cart = createSpyFromClass(CartService); // from a class
const gateway = createAutoMock<PaymentGateway>(); // from a type/interface aloneThe constructor never runs. Use createAutoMock<T>() when there is no class at run time: an
interface, a type alias, a .d.ts.
How to mock: an object the test already holds
createSpyFromInstance turns an existing object into a spy in place. Use it when other code already
holds a reference to the object (a closure, a DI container, a subscription).
import { createSpyFromInstance, restoreSpiedInstance } from 'vitest-auto-spy';
const client = new PaymentsClient(config); // real, and already wired into the code under test
const spy = createSpyFromInstance(client); // same object, now typed as Spy<PaymentsClient>
spy.charge.calledWith(100).resolveWith({ ok: true });
await checkout.pay(100);
expect(spy.charge).toHaveBeenCalledWith(100);
restoreSpiedInstance(client); // client.charge is the real method again- It takes most of the options of
createSpyFromClass. - With
{ passthrough: true }, each method keeps running its real code and records its calls, until you configure that method:createSpyFromInstance(TestBed.inject(CartService), { passthrough: true }). - To undo:
restoreSpiedInstance(instance),restoreMockedProps(), orusing spy = ….
How to mock: a plain data fixture
createMock<T>(partial) builds typed test data from the fields you pass. Use it instead of an
object literal with as T.
const device = { id: '1', name: 'TV', isOffline: false } as Device; // ❌ a key Device lacks still compiles
const device = createMock<Device>({ id: '1', name: 'TV' }); // ✅ checked at every depthUnnamed fields are undefined, as with the cast, but an unknown key is a compile error. Where the
value already sits in a typed slot (an argument, a typed const), just delete the cast. The
prefer-create-mock lint rule finds the casts.
Details
How to mock: reading a spy back from DI
const cart = injectSpy(CartService); // typed Spy<CartService>, no castCommon mistake: vi.spyOn(TestBed.inject(CartService), 'total'). It replaces one method and
leaves the rest of a real service running.
How to mock: a whole class's dependencies at once
createWithAutoSpies builds a class through Angular DI and spies every dependency you did not
provide:
import { createWithAutoSpies } from 'vitest-auto-spy/angular';
const { instance, spies } = createWithAutoSpies(CartService);
spies.get(PricingService).total.mockReturnValue(100);
instance.checkout(3);
expect(spies.get(PricingService).total).toHaveBeenCalled();Pass providers for the ones you want to control yourself; they win.
Details
How to mock: a readonly property or a signal
import { mockReadonlyProp, mockValueProp, restoreMockedProps } from 'vitest-auto-spy';
mockReadonlyProp(store, 'items', signal([task])); // readonly field, signal(), computed()
mockValueProp(service, 'retries', 3); // a field the code assigns toThese helpers remember what they replaced, and one restoreMockedProps() undoes them all.
setupAutoSpy() runs it after every test.
Common mistake: Object.defineProperty in a spec. Nothing undoes it, and under
isolate: false it leaks into the next file.
Details
How to mock: an Observable
import { expectEmission } from 'vitest-auto-spy';
cart.items$.nextWith([task]); // drive the stream from the spy
await expect(expectEmission(component.visible$)).resolves.toBe(true); // the first value, not a listCommon mistake: source$.subscribe(value => expect(value).toEqual(…)). If the stream never
emits, nothing is asserted and the test passes. expectEmission fails on a silent stream.
Details
How to mock: an overloaded method
A spy uses the last overload of a method by default. Name the one you mean on the declaration:
import type { Spy } from 'vitest-auto-spy';
let shelves: Spy<ShelvesClient, { overload: { getShelf: 'first' } }>;
shelves = createSpyFromClass(ShelvesClient); // no second type argument here
shelves.getShelf.nextWith(page); // checked against the first signatureCommon mistake: @ts-expect-error on the line. It also hides a fixture of the wrong shape. The
no-ts-expect-error-on-double rule reports it.
Details
How to mock: a call that has to throw
cart.checkout.failWith(new HttpErrorResponse({ status: 500 })); // every call throws
cart.checkout.calledWith(BAD_ID).failWith(new Error('unknown cart')); // only these argumentsfailWith works on a method of any return type, on every runner.
Details
How to mock: a promise a test forgets to await
// ❌ the test ends before the callback runs, so the assertion never runs at all
it('renders once compiled', () => {
TestBed.compileComponents().then(() => expect(component.ready).toBe(true));
});
// ✅ the assertion lands inside the test
it('renders once compiled', async () => {
await TestBed.compileComponents();
expect(component.ready).toBe(true);
});Under zone.js, an assertion that fails inside an un-awaited .then() does not even reach the runner.
setupAutoSpy({ strayRejections: true }) makes it fail the test, and the no-floating-assertion
lint rule finds the shape before it runs.
Details
How to mock: a component's children
import { provideAutoSpy, renderShallow } from 'vitest-auto-spy/angular';
const { fixture, component } = renderShallow(TaskListComponent, {
providers: [provideAutoSpy(TaskService)],
inputs: { projectId: 42 },
});renderShallow renders through the real TestBed without child components. fixture is a real
ComponentFixture. keepTemplate and keepChildren keep parts of the template:
Shallow component rendering.
How to mock: a child the template still binds
createComponentStub builds a stand-in child from the real one's selector, inputs and outputs, so
it cannot drift:
import { createComponentStub } from 'vitest-auto-spy/angular';
const ChartStub = createComponentStub(ChartComponent);
TestBed.configureTestingModule({ imports: [DashboardComponent] });
TestBed.overrideComponent(DashboardComponent, { remove: { imports: [ChartComponent] }, add: { imports: [ChartStub] } });
const chart = fixture.debugElement.query(By.directive(ChartStub)).componentInstance;
expect(chart.series()).toEqual([1, 2, 3]);
chart.pointSelected.emit(2);Directives and pipes work the same way. With renderShallow: { keepTemplate: true, keepChildren: [ChartStub] }.
Details
How to mock: a lifecycle hook
Spy on the prototype, before the component is created:
const init = vi.spyOn(CardComponent.prototype, 'ngOnInit').mockImplementation(() => undefined);
const fixture = TestBed.createComponent(CardComponent);
fixture.detectChanges();
expect(init).toHaveBeenCalledTimes(1);Common mistake: vi.spyOn(component, 'ngOnInit') on the instance. Angular never calls it, so
the real hook runs. The no-instance-lifecycle-spy rule reports it. Better still, assert what the
hook does.
How to mock: a class the code under test builds with new
createSpyClass is a real constructor whose instances are full spies:
import { createSpyClass, mockValueProp } from 'vitest-auto-spy';
const WorkerSpy = createSpyClass(BackgroundWorker);
mockValueProp(globalThis, 'BackgroundWorker', WorkerSpy);
service.start();
expect(WorkerSpy.calls[0]).toEqual(['./task.js']);
WorkerSpy.instances[0].postMessage.mockReturnValue(undefined);callsholds the constructor arguments,instancesthe spy each call produced.createSpyClass(BackgroundWorker, undefined, { statics: true })also copies static members: functions become spies, data is copied, getters are skipped.- With no class at run time (a browser global, a vendor SDK), use
mockConstructor, orstubConstructor(globalThis, 'Image', () => ({ src: '' })), whichrestoreMockedProps()removes.
Common mistake: vi.fn().mockImplementation(() => obj) called with new. An arrow function is
not a constructor, so you get TypeError: … is not a constructor or an empty object.
Details
How to mock: a double more than one spec uses
// ❌ a constant: one set of spies for the whole worker
export const actionContext = { actions: { navigateToSection: vi.fn() } };
// ✅ a factory: one set per caller
export const createActionContext = () => ({ actions: { navigateToSection: vi.fn() } });Under isolate: false a module runs once per worker, so an exported constant is shared by every
file. Put shared doubles in a *.mock.ts file, and never export from a spec file. The
no-shared-module-level-mock rule finds these.
How to mock: a pipe
A pipe is a class with one method:
const currency = createSpyFromClass(CurrencyPipe);
currency.transform.calledWith(10, 'EUR').mockReturnValue('€10');In a component spec, provide it with provideAutoSpy(CurrencyPipe), or drop it with renderShallow.
To keep the template and replace the pipe in it:
createComponentStub(CurrencyPipe, { transform: (value) => String(value) }).
How to mock: localStorage and sessionStorage
import { stubWebStorage } from 'vitest-auto-spy/dom-stubs';
const local = stubWebStorage('localStorage', { items: { token: 'abc' } });
session.logout();
expect(local.snapshot()).toEqual({});An in-memory Storage, installed in beforeEach and removed by restoreMockedProps().
Details
How to mock: a Web Worker
import { stubWorker } from 'vitest-auto-spy/dom-stubs';
const workers = stubWorker<TreeRequest, TreeResponse>({
respond: (request) => ({ requestId: request.requestId, nodes: [] }),
});
await firstValueFrom(service.visibleNodes(request));
expect(workers.last.messages).toEqual([request]);respond answers each message on a microtask. Without it, drive the worker with
workers.last.emit(data) / fail(error).
Details
How to mock: fetch and other globals
import { mockValueProp } from 'vitest-auto-spy';
import { stubResponse } from 'vitest-auto-spy/setup';
beforeEach(() => {
mockValueProp(
globalThis,
'fetch',
vi.fn(async () => stubResponse({ body: user })),
);
});mockValuePropputs the original back after each test (throughsetupAutoSpy()).stubResponsebuilds a realResponse: data goes out as JSON (nullincluded);undefinedor nobodysends no body. A body can be read once, so build a new one per call.- To only keep tests off the network, use
blockNetwork().
Common mistake: global.fetch = vi.fn(). No cleanup reaches it, so it answers every later test
of the file. The no-hand-assigned-global rule reports it.
Details
How to mock: the console
import { useConsoleSpies } from 'vitest-auto-spy/console';
const { consoleErrorSpy } = useConsoleSpies();
it('reports a failed load', () => {
service.load();
expect(consoleErrorSpy).toHaveBeenCalledWith('load failed', expect.any(Error));
});useConsoleSpies() installs silent spies before each test and restores the console after. It works
on every runner. consoleOutput() returns everything logged, per channel, for one exact assertion.
Common mistakes:
- A bare
vi.spyOn(console, 'error')still prints; add.mockImplementation(() => undefined). - Relying on the import alone: it installs once per worker, and under
isolate: falsesilences every later file.
setupAutoSpy({ strayConsole: 'throw' }) fails any test whose output nothing absorbed.
Details
How to mock: a jasmine suite mid-migration
vitest-auto-spy/jasmine keeps jasmine's .and, .calls and .withArgs working, so changing the
import is enough to run the suite:
import { type Spy, createSpyFromClass } from 'vitest-auto-spy/jasmine';
const service: Spy<AccountService> = createSpyFromClass(AccountService);
service.load.and.returnValue('stubbed'); // .mockReturnValue(…) once the shim is gone
service.load.and.nextWith(account); // .nextWith(…) once the shim is gone
service.load.withArgs(7).and.returnValue('seven'); // .calledWith(7).mockReturnValue(…)
expect(service.load.calls.count()).toBe(1); // service.load.mock.calls.lengthOn bun test or node --test, call enableJasmineCompat() from vitest-auto-spy/jasmine-compat in
the setup file instead. Spies created before that call do not get the jasmine API.
npx vitest-auto-spy codemod --from jasmine rewrites the calls.
Details
Everything else
npmjs.com shows a README only up to its first 64 kB, so this is where the npm page ends. Next:
- Getting started: a first spec, step by step.
- Installation: wiring for
ng test, Vitest, Bun,node:testand Rstest. - Spec patterns: the shapes a large Angular suite settled on.
- API: every export.
- The CLI and ESLint rules.
- Migrating from jest-auto-spies or from jasmine-auto-spies.
- Agents: pointing a coding agent at the
AGENTS.mdinside the package.
The whole README, with its full table of contents, is on GitHub.
