Skip to content
Vitest 3
Main Navigation 指南 & API配置浏览器模式高级 API
3.2.0
2.1.9
1.6.1
0.34.6

简体中文

English
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

简体中文

English
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

主题

Sidebar Navigation

简介

为什么选择 Vitest

快速入门

特性

配置 Vitest

API

测试 API 参考

模拟函数

Vi

expect

expectTypeOf

assert

assertType

指南

命令行界面

测试过滤

测试项目

报告器

覆盖率

快照

模拟

并行

类型测试

Vitest UI

源内测试

测试上下文

测试注解

测试环境

扩展匹配器

IDE 集成

调试

常见错误

迁移指南

迁移到 Vitest 3.0

从 Jest 迁移

性能

分析测试性能

性能优化

浏览器模式

高级 API

与其他测试运行器的比较

页面导航

模拟函数 ​

你可以使用 vi.fn 方法创建一个模拟函数来追踪其调用。如果你想追踪已创建对象的方法,可以使用 vi.spyOn 方法:

js
import { vi } from 'vitest';

const fn = vi.fn();
fn('hello world');
fn.mock.calls[0] === ['hello world'];

const market = {
  getApples: () => 100,
};

const getApplesSpy = vi.spyOn(market, 'getApples');
market.getApples();
getApplesSpy.mock.calls.length === 1;

你应该结合 expect 使用模拟断言(例如,toHaveBeenCalled)来断言模拟结果。本 API 参考介绍了可用于控制模拟行为的属性和方法。

TIP

下面类型定义中的自定义函数实现用泛型 <T> 标记。

getMockImplementation ​

ts
function getMockImplementation(): T | undefined;

返回当前的模拟实现(如果存在)。

如果模拟是使用 vi.fn 创建的,它将使用提供的方法作为模拟实现。

如果模拟是使用 vi.spyOn 创建的,除非提供了自定义实现,否则它将返回 undefined。

getMockName ​

ts
function getMockName(): string;

使用此方法返回通过 .mockName(name) 方法分配给模拟的名称。默认情况下,它将返回 vi.fn()。

mockClear ​

ts
function mockClear(): MockInstance<T>;

清除每次调用的所有信息。调用此方法后,.mock 上的所有属性都将恢复到其初始状态。此方法不会重置实现。它对于在不同断言之间清理模拟很有用。

ts
const person = {
  greet: (name: string) => `Hello ${name}`,
};
const spy = vi.spyOn(person, 'greet').mockImplementation(() => 'mocked');
expect(person.greet('Alice')).toBe('mocked');
expect(spy.mock.calls).toEqual([['Alice']]);

// 清除调用历史,但保留模拟实现
spy.mockClear();
expect(spy.mock.calls).toEqual([]);
expect(person.greet('Bob')).toBe('mocked');
expect(spy.mock.calls).toEqual([['Bob']]);

要自动在每次测试之前调用此方法,请在配置中启用 clearMocks 设置。

mockName ​

ts
function mockName(name: string): MockInstance<T>;

设置内部模拟名称。这在断言失败时有助于识别模拟函数。

mockImplementation ​

ts
function mockImplementation(fn: T): MockInstance<T>;

接受一个函数作为模拟实现。TypeScript 要求参数和返回类型与原始函数匹配。

ts
const mockFn = vi.fn().mockImplementation((apples: number) => apples + 1);
// 或者: vi.fn(apples => apples + 1);

const NelliesBucket = mockFn(0);
const BobsBucket = mockFn(1);

NelliesBucket === 1; // true
BobsBucket === 2; // true

mockFn.mock.calls[0][0] === 0; // true
mockFn.mock.calls[1][0] === 1; // true

mockImplementationOnce ​

ts
function mockImplementationOnce(fn: T): MockInstance<T>;

接受一个函数作为模拟实现。TypeScript 要求参数和返回类型与原始函数匹配。此方法可以链式调用,从而为多次函数调用提供不同的结果。

ts
const myMockFn = vi
  .fn()
  .mockImplementationOnce(() => true) // 第一次调用
  .mockImplementationOnce(() => false); // 第二次调用

myMockFn(); // 第一次调用: true
myMockFn(); // 第二次调用: false

当模拟函数的特定实现用尽后,如果之前调用了 vi.fn(() => defaultValue) 或 .mockImplementation(() => defaultValue),它将转而调用默认实现:

ts
const myMockFn = vi
  .fn(() => 'default')
  .mockImplementationOnce(() => 'first call')
  .mockImplementationOnce(() => 'second call');

// 'first call', 'second call', 'default', 'default'
console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());

withImplementation ​

ts
function withImplementation(fn: T, cb: () => void): MockInstance<T>;
function withImplementation(
  fn: T,
  cb: () => Promise<void>
): Promise<MockInstance<T>>;

在回调执行期间暂时覆盖原始模拟实现。

js
const myMockFn = vi.fn(() => 'original');

myMockFn.withImplementation(
  () => 'temp',
  () => {
    myMockFn(); // 'temp'
  }
);

myMockFn(); // 'original'

可以与异步回调一起使用。需要等待该方法执行完成,以便之后使用原始实现。

ts
test('async callback', async () => {
  const myMockFn = vi.fn(() => 'original');

  // 我们等待此调用,因为回调是异步的
  await myMockFn.withImplementation(
    () => 'temp',
    async () => {
      myMockFn(); // 'temp'
    }
  );

  myMockFn(); // 'original'
});

请注意,此方法优先于 mockImplementationOnce。

mockRejectedValue ​

ts
function mockRejectedValue(value: unknown): MockInstance<T>;

接受一个错误,当异步函数被调用时,该错误将导致 Promise 被拒绝。

ts
const asyncMock = vi.fn().mockRejectedValue(new Error('Async error'));

await asyncMock(); // 抛出 Error<'Async error'>

mockRejectedValueOnce ​

ts
function mockRejectedValueOnce(value: unknown): MockInstance<T>;

接受一个值,该值将在下一次函数调用期间导致 Promise 被拒绝。如果链式调用,每次连续调用都将拒绝指定的值。

ts
const asyncMock = vi
  .fn()
  .mockResolvedValueOnce('first call')
  .mockRejectedValueOnce(new Error('Async error'));

await asyncMock(); // 'first call'
await asyncMock(); // 抛出 Error<'Async error'>

mockReset ​

ts
function mockReset(): MockInstance<T>;

执行 mockClear 的功能,并将内部实现重置为原始函数。 这也会重置所有“一次性”实现。

请注意,重置通过 vi.fn() 创建的模拟函数会将实现设置为返回 undefined 的空函数。 重置 vi.fn(impl) 的模拟会将实现恢复为 impl。

当您想要将模拟重置为其原始状态时,此方法很有用。

ts
const person = {
  greet: (name: string) => `Hello ${name}`,
};
const spy = vi.spyOn(person, 'greet').mockImplementation(() => 'mocked');
expect(person.greet('Alice')).toBe('mocked');
expect(spy.mock.calls).toEqual([['Alice']]);

// 清除调用历史并重置实现,但方法仍被监视
spy.mockReset();
expect(spy.mock.calls).toEqual([]);
expect(person.greet).toBe(spy);
expect(person.greet('Bob')).toBe('Hello Bob');
expect(spy.mock.calls).toEqual([['Bob']]);

要自动在每次测试之前调用此方法,请在配置中启用 mockReset 设置。

mockRestore ​

ts
function mockRestore(): MockInstance<T>;

执行 mockReset 的功能,并恢复被监视对象的原始方法。

请注意,恢复 vi.fn() 的模拟会将实现设置为返回 undefined 的空函数。 恢复 vi.fn(impl) 的模拟会将实现恢复为 impl。

ts
const person = {
  greet: (name: string) => `Hello ${name}`,
};
const spy = vi.spyOn(person, 'greet').mockImplementation(() => 'mocked');
expect(person.greet('Alice')).toBe('mocked');
expect(spy.mock.calls).toEqual([['Alice']]);

// 清除调用历史并恢复被监视对象的方法
spy.mockRestore();
expect(spy.mock.calls).toEqual([]);
expect(person.greet).not.toBe(spy);
expect(person.greet('Bob')).toBe('Hello Bob');
expect(spy.mock.calls).toEqual([]);

要自动在每次测试之前调用此方法,请在配置中启用 restoreMocks 设置。

mockResolvedValue ​

ts
function mockResolvedValue(value: Awaited<ReturnType<T>>): MockInstance<T>;

接受一个值,当异步函数被调用时,该值将作为 Promise 的解决值。TypeScript 只接受与原始函数返回类型匹配的值。

ts
const asyncMock = vi.fn().mockResolvedValue(42);

await asyncMock(); // 42

mockResolvedValueOnce ​

ts
function mockResolvedValueOnce(value: Awaited<ReturnType<T>>): MockInstance<T>;

接受一个值,该值将在下一次函数调用期间作为 Promise 的解决值。TypeScript 只接受与原始函数返回类型匹配的值。如果链式调用,每次连续调用都将解析指定的值。

ts
const asyncMock = vi
  .fn()
  .mockResolvedValue('default')
  .mockResolvedValueOnce('first call')
  .mockResolvedValueOnce('second call');

await asyncMock(); // first call
await asyncMock(); // second call
await asyncMock(); // default
await asyncMock(); // default

mockReturnThis ​

ts
function mockReturnThis(): MockInstance<T>;

如果你需要让方法返回其自身的 this 上下文,而不执行实际的实现,请使用此方法。这是以下代码的简写:

ts
spy.mockImplementation(function () {
  return this;
});

mockReturnValue ​

ts
function mockReturnValue(value: ReturnType<T>): MockInstance<T>;

接受一个值,该值将在每次调用模拟函数时返回。TypeScript 只接受与原始函数返回类型匹配的值。

ts
const mock = vi.fn();
mock.mockReturnValue(42);
mock(); // 42
mock.mockReturnValue(43);
mock(); // 43

mockReturnValueOnce ​

ts
function mockReturnValueOnce(value: ReturnType<T>): MockInstance<T>;

接受一个值,该值将在每次调用模拟函数时返回。TypeScript 只接受与原始函数返回类型匹配的值。

当模拟函数的特定实现用尽后,如果之前调用了 vi.fn(() => defaultValue) 或 .mockImplementation(() => defaultValue),它将转而调用默认实现:

ts
const myMockFn = vi
  .fn()
  .mockReturnValue('default')
  .mockReturnValueOnce('first call')
  .mockReturnValueOnce('second call');

// 'first call', 'second call', 'default', 'default'
console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());

mock.calls ​

ts
const calls: Parameters<T>[];

这是一个数组,包含每次调用的所有参数。数组中的每个项都是一次调用的参数列表。

js
const fn = vi.fn();

fn('arg1', 'arg2');
fn('arg3');

fn.mock.calls ===
  [
    ['arg1', 'arg2'], // 第一次调用
    ['arg3'], // 第二次调用
  ];

mock.lastCall ​

ts
const lastCall: Parameters<T> | undefined;

此属性包含最后一次调用的参数。如果模拟未被调用,它将返回 undefined。

mock.results ​

ts
interface MockResultReturn<T> {
  type: 'return';
  /**
   * 函数返回的值。
   * 如果函数返回 Promise,则这将是已解决的值。
   */
  value: T;
}

interface MockResultIncomplete {
  type: 'incomplete';
  value: undefined;
}

interface MockResultThrow {
  type: 'throw';
  /**
   * 函数执行期间抛出的错误。
   */
  value: any;
}

type MockResult<T> =
  | MockResultReturn<T>
  | MockResultThrow
  | MockResultIncomplete;

const results: MockResult<ReturnType<T>>[];

这是一个数组,包含函数每次执行的结果。数组中的每个项都是一个包含 type 和 value 属性的对象。可用类型有:

  • 'return' - 函数返回而没有抛出错误。
  • 'throw' - 函数抛出了一个值。

value 属性包含返回的值或抛出的错误。如果函数返回一个 Promise,那么其结果的 type 将始终是 'return',即使该 Promise 最终被拒绝。

js
const fn = vi
  .fn()
  .mockReturnValueOnce('result')
  .mockImplementationOnce(() => {
    throw new Error('thrown error');
  });

const result = fn(); // 返回 'result'

try {
  fn(); // 抛出 Error
} catch {}

fn.mock.results ===
  [
    // 第一个结果
    {
      type: 'return',
      value: 'result',
    },
    // 最后一个结果
    {
      type: 'throw',
      value: Error,
    },
  ];

mock.settledResults ​

ts
interface MockSettledResultFulfilled<T> {
  type: 'fulfilled';
  value: T;
}

interface MockSettledResultRejected {
  type: 'rejected';
  value: any;
}

export type MockSettledResult<T> =
  | MockSettledResultFulfilled<T>
  | MockSettledResultRejected;

const settledResults: MockSettledResult<Awaited<ReturnType<T>>>[];

一个数组,包含函数返回的 Promise 最终解决或拒绝的所有值。

如果函数返回的 Promise 尚未解决或拒绝,则此数组将为空。

js
const fn = vi.fn().mockResolvedValueOnce('result');

const result = fn();

fn.mock.settledResults === [];

await result;

fn.mock.settledResults ===
  [
    {
      type: 'fulfilled',
      value: 'result',
    },
  ];

mock.invocationCallOrder ​

ts
const invocationCallOrder: number[];

此属性返回模拟函数的执行顺序。它是一个数字数组,该顺序在所有已定义的模拟之间是共享的。

js
const fn1 = vi.fn();
const fn2 = vi.fn();

fn1();
fn2();
fn1();

fn1.mock.invocationCallOrder === [1, 3];
fn2.mock.invocationCallOrder === [2];

mock.contexts ​

ts
const contexts: ThisParameterType<T>[];

此属性是一个数组,包含每次调用模拟函数时所使用的 this 值。

js
const fn = vi.fn();
const context = {};

fn.apply(context);
fn.call(context);

fn.mock.contexts[0] === context;
fn.mock.contexts[1] === context;

mock.instances ​

ts
const instances: ReturnType<T>[];

此属性是一个数组,包含当模拟作为构造函数(使用 new 关键字)调用时创建的所有实例。请注意,这里存储的是函数的实际 this 上下文,而非其返回值。

WARNING

如果模拟是用 new MyClass() 实例化的,那么 mock.instances 将是一个包含一个值的数组:

js
const MyClass = vi.fn();
const a = new MyClass();

MyClass.mock.instances[0] === a;

如果你从构造函数返回一个值,它将不会出现在 instances 数组中,而是会出现在 results 数组中:

js
const Spy = vi.fn(() => ({ method: vi.fn() }));
const a = new Spy();

Spy.mock.instances[0] !== a;
Spy.mock.results[0] === a;
Pager
上一页测试 API 参考
下一页Vi

基于 MIT 许可证 发布。

版权所有 (c) 2021-Present Vitest Team

https://vitest.dev/api/mock

基于 MIT 许可证 发布。

版权所有 (c) 2021-Present Vitest Team