new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Vitest · Guide · all subjects

mocking functions & modules

52 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

clearMocks defaults to true in Vitest 5.0

In Vitest 5.0, the clearMocks option now defaults to true. This means vi.clearAllMocks() is called before every test, resetting mock.calls, mock.instances, mock.contexts and mock.results. Mock implementations are left intact. To restore the previous behavior where mock history persists between tests, set clearMocks: false in the vitest config.

vi.mock, vi.unmock, and vi.hoisted must be at top level

In Vitest 5.0, vi.mock(), vi.unmock(), and vi.hoisted() calls must be at the module's top level. Calling them inside functions, blocks, or describe/test callbacks now throws an error instead of logging a warning. The error reports every offending call and its location. Dynamic variants vi.doMock() and vi.doUnmock() are not hoisted and can be called anywhere.

Class mocks inherit from implementation prototype in Vitest 5.0

In Vitest 5.0, instances created from a class mock now inherit from the implementation's prototype. Methods defined with class syntax are now available on instances, instanceof checks work correctly, and the mock's prototype is chained to the implementation's prototype. This applies to vi.fn(Dog), vi.spyOn(obj, 'Dog'), and mockImplementation(class ...). mockReset reverts the chain to the original class.

Chai-style spy assertions in Vitest

Vitest supports Chai-style assertions for spies and mocks: called, calledOnce, calledTwice, calledThrice, callCount(n), calledWith(...), calledOnceWith(...), returned(value). These assertions work the same as sinon-chai for migration from Mocha+Chai+Sinon.

Vi API spy methods in Vitest

In Vitest, create spies with vi.fn(), vi.spyOn(obj, 'method'). Stub return values with mockReturnValue(value) and mockReturnValueOnce(value). Stub implementations with mockImplementation(fn). Restore spies with mockRestore() or vi.restoreAllMocks().

Module mocking factory function in Vitest

When mocking a module in Vitest with vi.mock(), the factory argument must return an object with each export explicitly defined. For a default export, include default: 'value' in the returned object.

vi.importActual replaces jest.requireActual

In Vitest, use vi.importActual() instead of Jest's jest.requireActual() to import the original of a mocked package. It returns a promise.

Inline mocking external libraries with server.deps.inline

To extend mocking of a module to external libraries that use it in Vitest, use server.deps.inline configuration to mark which 3rd-party libraries should be part of your source code and subject to mocking.

vi.stubEnv and vi.spyOn for environment properties

In Vitest, use vi.stubEnv() or vi.spyOn() to modify environment properties, equivalent to Jest's replaceProperty API.

Mock entire class with vi.fn

You can mock an entire class with a single vi.fn call. Wrap the class definition inside vi.fn() to create a fully mocked class where all methods become mock functions.

Class fields vs prototype methods in mocking

Class fields (e.g., greet = () => {}) are assigned during construction, so every instance gets its own copy as its own property. Prototype methods (e.g., speak()) are created once and stored on the class prototype, shared by all instances. This distinction matters for mocking because fields give each instance separate mocks while prototype methods are shared.

Mock class with all methods as class fields

When recreating a class mock with vi.fn, define every method as a class field using vi.fn. This allows each instance to get its own separate mock, which enables checking calls on a single instance without interference from other instances.

Constructor returning non-primitive breaks instanceof

If a non-primitive is returned from the constructor function, that value becomes the result of the new expression. In this case the [[Prototype]] may not be correctly bound and instanceof checks will fail. Prefer class syntax over function syntax when mocking classes.

Mock class by wrapping in module factory

When mocking a class that is re-exported from another module, recreate the class inside the module factory passed to vi.mock(). Define all methods as vi.fn() and return the mocked class in the factory function.

Pass mocked class instance to function

You can use a mocked class instance created with vi.fn to pass to a function that accepts the same interface. This allows testing functions that take class instances as parameters.

Each class field instance gets separate mock

When methods are defined as class fields in a mocked class, each instance gets its own separate mock function. This means Max.speak and Cooper.speak are different mock functions with separate call histories, allowing you to verify calls on specific instances.

Prototype chain preserved with vi.fn class

Instances keep the prototype chain of the class you pass to vi.fn, so prototype methods stay available on instances during and after construction, and instances pass instanceof checks against that class.

Prototype methods not mocked by default

When using vi.fn on a class without redefining methods as class fields, prototype methods are not automatically mocked. The instance finds the method through the prototype chain and refers to the original class method, so call assertions will throw an error.

Mock prototype method for all instances

To mock a prototype method for all instances at once, assign a mock function directly to the prototype: MockedClass.prototype.methodName = vi.fn(). This shadows the original method and all instances will see the mock, even those created before the assignment. Calls from all instances are recorded by the same mock.

Prototype lookup order for mocked classes

The lookup order for prototype methods is: instance → MockedClass.prototype → OriginalClass.prototype. Assigning a mock on MockedClass.prototype keeps the original class untouched by shadowing the original method.

Mock prototype re-pointed on implementation change

The mock's prototype always follows the current implementation. It is re-pointed when you set a new implementation, when a queued mockImplementationOnce class is constructed, and when the mock is reset. If a single mock uses different class implementations, instances created by earlier implementations lose access to their prototype methods once a newer implementation takes over.

Mock single instance method with vi.spyOn

Use vi.spyOn to mock the method of one instance only. It defines the mock directly on that instance, shadowing the prototype method just for it. Other instances will still use the original or prototype-level mock.

Reassign return value for specific instance

When methods are defined as class fields, each instance has its own mock. You can reassign the return value for a specific instance directly using vi.mocked(instance.method).mockReturnValue().

Mock non-function property with vi.spyOn

To mock a non-function property like a field, use vi.spyOn(instance, 'propertyName', 'get').mockReturnValue(). This makes it possible to use spy assertions on the mocked property.

Spy on getters and setters

You can spy on both getters and setters using vi.spyOn with the 'get' or 'set' accessor type, enabling assertions on property access.

vi.mocked type helper for mocked classes

vi.mocked is a type helper that wraps a function in a Mock<T> type. Use it to properly type mocked class instances in TypeScript when the type system doesn't know a class is mocked.

vi.fn with classes introduced in Vitest 4

Using classes with vi.fn() was introduced in Vitest 4. Previously, you had to use function and prototype inheritance directly.

Example: mock entire Dog class

const Dog = vi.fn(class { static getType = vi.fn(() => 'mocked animal') constructor(name) { this.name = name } greet = vi.fn(() => `Hi! My name is ${this.name}!`) speak = vi.fn(() => 'loud bark!') feed = vi.fn() })

Example: test function with mocked class instance

import { expect, test, vi } from 'vitest' import { feed } from '../src/feed.js' const Dog = vi.fn(class { feed = vi.fn() isHungry = vi.fn(() => false) }) test('can feed dogs', () => { const dogMax = new Dog('Max') feed(dogMax) expect(dogMax.feed).toHaveBeenCalled() expect(dogMax.isHungry()).toBe(false) })

Example: mock prototype method for all instances

MockedDog.prototype.speak = vi.fn(() => 'woof!') const cooper = new MockedDog('Cooper') const max = new MockedDog('Max') cooper.speak() // woof! max.speak() // woof! expect(MockedDog.prototype.speak).toHaveBeenCalledTimes(2) expect(vi.mocked(MockedDog.prototype.speak).mock.contexts).toEqual([cooper, max])

Example: spy on single instance method

const cooper = new MockedDog('Cooper') const max = new MockedDog('Max') vi.spyOn(cooper, 'speak').mockReturnValue('meow!') cooper.speak() // meow! max.speak() // bark!, still the original method expect(cooper.speak).toHaveBeenCalledTimes(1)

Example: mock non-function property with getter spy

const dog = new Dog('Cooper') const nameSpy = vi.spyOn(dog, 'name', 'get').mockReturnValue('Max') expect(dog.name).toBe('Max') expect(nameSpy).toHaveBeenCalledTimes(1)

vi.when API for conditional mocking

vi.when (available in version 5.0.0 and later) takes a spy and lets you define argument-specific behaviors. Call .calledWith(...args) to declare which arguments to match, creating a behavior. Then attach an action by calling a then* method to determine what happens when the behavior matches. Multiple behaviors can be chained on the same spy.

vi.when action methods and equivalents

The available action methods are: thenReturn(value) equivalent to mockReturnValue(value), thenThrow(error) equivalent to mockThrow(error), thenResolve(value) equivalent to mockResolvedValue(value), thenReject(error) equivalent to mockRejectedValue(error).

vi.when stacking multiple actions on one behavior

A single behavior can have multiple actions attached to it. When the behavior matches, actions are consumed in last-in-first-out order: the most recently registered action runs first. Once that action has been consumed, Vitest falls back to the previous one. Use the times option to limit how many calls an action handles before falling through to the next action. An action with no times limit runs indefinitely. Because actions are evaluated in reverse registration order, indefinite actions should be registered first so that later finite actions can temporarily override them.

vi.when then*Once shorthand methods

For convenience, then*Once shorthands are available and equivalent to { times: 1 }: thenReturnOnce, thenResolveOnce, thenThrowOnce, thenRejectOnce.

vi.when with asymmetric matchers

calledWith supports asymmetric matchers like expect.stringContaining(), expect.any(), etc. This is useful when you care about the shape or type of an argument rather than its exact value. Behaviors are matched in first-in-first-out order, so the first behavior whose arguments match the call wins. Specific matchers must therefore be registered before broad ones.

vi.when behavior merging with asymmetric matchers

When registering a new behavior, Vitest checks existing behaviors in registration order. If the new arguments already match an existing behavior, the new action is merged into that behavior instead of creating a new one. This is especially important with broad asymmetric matchers. For example, if you register calledWith(expect.any(String)).thenReturn('user'), then later register calledWith('admin@example.com').thenReturnOnce('admin'), the 'admin' action is not scoped to 'admin@example.com' but becomes the next action for the entire expect.any(String) behavior.

vi.when unmatched calls default behavior

By default, when the spy is called with arguments that match no registered behavior, it falls back to the spy's original implementation. If the spy has no original implementation, it returns undefined.

vi.when onUnmatched throw option

Pass { onUnmatched: 'throw' } to throw whenever the spy is called with unregistered arguments. The error message includes the unmatched arguments and is formatted as 'vi.when: no behavior defined when called with [args]'. The error type and message are fixed and cannot be customized.

vi.when onUnmatched custom function

Pass a function to the onUnmatched option to handle unmatched calls with custom logic, for example when a shared mock needs a different fallback per test. The function is called with the same arguments as the spy and its return value is used directly as the spy's result. If it throws or returns a rejected promise, that error propagates to the caller just as it would from any action.

vi.when asymmetric matcher as catch-all

Registering a broad calledWith last acts as a fallback for calls that do not match any earlier, more specific behavior. The fallback behavior can return a specific value, resolve or reject a promise, or throw a typed error.

vi.when toHaveBeenExhausted assertion

The object returned by vi.when supports the toHaveBeenExhausted assertion to check that all registered behaviors were actually matched and their actions consumed. If not all behaviors are exhausted, the test fails with a message listing the behaviors that were never matched.

vi.when toHaveBeenExhausted caveats

A vi.when chain with no behaviors is never considered exhausted. The same applies to a bare .calledWith() with no then* action attached. Both will always cause toHaveBeenExhausted to fail. Indefinite actions (no times limit) satisfy exhaustion checks after being used at least once, and the actions keep responding after that but the assertion is satisfied.

vi.when automatic cleanup with using

vi.when supports the Explicit Resource Management protocol. Declare the chain with using to scope behaviors to the current block and restore the spy automatically when execution leaves it.

vi.when example with multiple behaviors

Example showing vi.when with multiple calledWith behaviors: import { test, vi } from 'vitest'; test('returns user data', async () => { const db = { findById: vi.fn<FindById>() }; vi.when(db.findById).calledWith(1).thenResolve({ id: 1, name: 'Ella' }).calledWith(2).thenResolve({ id: 2, name: 'Gracie' }); await expect(getUserById(db, 1)).resolves.toEqual({ name: 'Ella' }); await expect(getUserById(db, 2)).resolves.toEqual({ name: 'Gracie' }); });

vi.when example with stacked actions and times option

Example showing stacked actions with times limit: test('retries after an initial failure', async () => { const fetchInstance = vi.fn<() => Promise<unknown>>(); vi.when(fetchInstance).calledWith('/data/config.json').thenResolve(new Response('{ debug: true }')).thenReject(new Error('network error'), { times: 1 }); await expect(readConfig(fetchInstance)).resolves.toEqual({ debug: true }); expect(fetchInstance).toHaveBeenCalledTimes(2); });

vi.when example with asymmetric matchers

Example showing asymmetric matchers with calledWith and order importance: test('sends email to each recipient', () => { vi.when(sendEmail).calledWith(expect.stringContaining('@internal.example.com')).thenReturn({ ok: true, message: 'sent via internal relay' }).calledWith(expect.stringContaining('@')).thenReturn({ ok: true, message: 'sent via external relay' }); });

vi.when example with onUnmatched throw

Example showing onUnmatched 'throw' option: vi.when(db.findById, { onUnmatched: 'throw' }).calledWith(1).thenResolve({ id: 1, name: 'Ella' }); await expect(db.findById(1)).resolves.toMatchObject({ name: 'Ella' }); await expect(db.findById(3)).rejects.toThrow('vi.when: no behavior defined when called with [3]');

vi.when example with onUnmatched function

Example showing onUnmatched with custom function: vi.when(db.findById, { onUnmatched: id => Promise.resolve({ id, name: `User ${id}` }) }).calledWith(1).thenResolve({ id: 1, name: 'Ella' }); await expect(db.findById(1)).resolves.toMatchObject({ name: 'Ella' }); await expect(db.findById(42)).resolves.toMatchObject({ name: 'User 42' });

vi.when example with toHaveBeenExhausted

Example showing toHaveBeenExhausted assertion: test('loads both users', async () => { const db = { findById: vi.fn<FindById>() }; const w = vi.when(db.findById).calledWith(1).thenResolveOnce({ id: 1, name: 'Ella' }).calledWith(2).thenResolveOnce({ id: 2, name: 'Gracie' }); await loadDashboard(db); expect(w).toHaveBeenExhausted(); });

vi.when example with using for auto-cleanup

Example showing using keyword for automatic cleanup: const spy = vi.fn(() => 'original'); test('with mocked behavior', () => { using w = vi.when(spy).calledWith('hello').thenReturn('mocked'); expect(spy('hello')).toBe('mocked'); }); test('without mocked behavior', () => { expect(spy('hello')).toBe('original'); });

Give your agent this brain