进阶 vitest.dev 2026-10-22 19:00:41 · 16 阅读

第6章 Mocking(模拟测试)指南与速查表

Mocking

TIP

如果你是 mocking 新手,可以从 Mock Functions 教程入手,通过实践了解 vi.fn、vi.spyOn 和 vi.mock。

编写测试时,迟早需要创建内部或外部服务的“伪”版本,这就是常说的 mocking。Vitest 通过 vi 辅助对象提供了一系列实用函数来帮你实现。你可以从 vitest 导入它;若已开启全局配置,也可在全局直接访问。

WARNING

务必记得在每次测试运行前后清除或恢复 mocks,以免上一轮的状态变化影响下一轮。详见 mockReset 文档。

如果你还不太熟悉 vi.fn、vi.mock 或 vi.spyOn 方法,请先查看 API 部分。

Vitest 提供了关于 mocking 的全面指南:

Mocking Classes

Mocking Dates

Mocking the File System

Mocking Functions

Mocking Globals

Mocking Modules

Mocking Requests

Mocking Timers

如果你想要更简单、快速的上手方式,可以查看下方的 Cheat Sheet。

Cheat Sheet

I want to…

Mock 导出的变量

example.js

export const getter = 'variable'

example.test.ts

import * as exports from './example.js'

vi.spyOn(exports, 'getter', 'get').mockReturnValue('mocked')

WARNING

此写法在 Browser Mode 下无法生效。替代方案见 Limitations。

Mock 导出的函数

示例(使用 vi.mock):

WARNING

注意 vi.mock 调用会被提升到文件顶部。它总是先于所有 import 执行。

example.js

export function method() {}

ts

import { method } from './example.js'

vi.mock('./example.js', () => ({ method: vi.fn() }))

示例(使用 vi.spyOn):

ts

import * as exports from './example.js'

vi.spyOn(exports, 'method').mockImplementation(() => {})

WARNING

vi.spyOn 示例在 Browser Mode 下无法生效。替代方案见 Limitations。

Mock 导出类的实现

示例(使用伪造类):

example.js

export class SomeClass {}

ts

import { SomeClass } from './example.js'

vi.mock(import('./example.js'), () => { const SomeClass = vi.fn(class FakeClass { someMethod = vi.fn() }) return { SomeClass } })

示例(使用 vi.spyOn):

ts

import * as mod from './example.js'

vi.spyOn(mod, 'SomeClass').mockImplementation(class FakeClass { someMethod = vi.fn() })

WARNING

vi.spyOn 示例在 Browser Mode 下无法生效。替代方案见 Limitations。

Spy 函数返回的对象

示例(使用缓存):

example.js

export function useObject() { return { method: () => true } }

useObject.js

import { useObject } from './example.js'

const obj = useObject() obj.method()

useObject.test.js

import { useObject } from './example.js'

vi.mock(import('./example.js'), () => { let _cache const useObject = () => { if (!_cache) { _cache = { method: vi.fn(), } } // 此后每次调用 useObject() 都会返回同一个对象引用 return _cache } return { useObject } })

const obj = useObject() // obj.method 曾在 some-path 中被调用 expect(obj.method).toHaveBeenCalled()

Mock 模块的一部分

ts

import { mocked, original } from './some-path.js'

vi.mock(import('./some-path.js'), async (importOriginal) => { const mod = await importOriginal() return { ...mod, mocked: vi.fn() } })

original() // 保留原有行为 mocked() // 是一个 spy 函数

WARNING

请注意,这里只 mock 了外部访问。在本例中,若 original 内部调用了 mocked,则永远会调用模块内定义的函数,而非 mock 工厂里的实现。

Mock 当前日期

Mock Date 和 Temporal 时间可以使用 vi.setSystemTime 辅助函数。该值不会在不同测试之间自动重置。

注意:使用 vi.useFakeTimers 也会改变 Date 的时间。

ts

const mockDate = new Date(2022, 0, 1) vi.setSystemTime(mockDate) const now = new Date() expect(now.valueOf()).toBe(mockDate.valueOf()) const nowInstant = Temporal.Now.instant() expect(nowInstant.epochMilliseconds).toBe(mockDate.valueOf()) // 重置被 mock 的时间 vi.useRealTimers()

Mock 全局变量

你可以通过给 globalThis 赋值来设置全局变量,也可以使用 vi.stubGlobal 辅助函数。使用 vi.stubGlobal 时,值不会在不同测试之间自动重置,除非你启用 unstubGlobals 配置选项,或手动调用 vi.unstubAllGlobals。

ts

vi.stubGlobal('__VERSION__', '1.0.0') expect(__VERSION__).toBe('1.0.0')

Mock import.meta.env

若要修改环境变量,直接给它赋新值即可。

WARNING

环境变量的值不会在不同测试之间自动重置。

ts

import { beforeEach, expect, it } from 'vitest'

// 你可以手动在 beforeEach 钩子中重置它 const originalViteEnv = import.meta.env.VITE_ENV

beforeEach(() => { import.meta.env.VITE_ENV = originalViteEnv })

it('changes value', () => { import.meta.env.VITE_ENV = 'staging' expect(import.meta.env.VITE_ENV).toBe('staging') })

如果你希望自动重置值,可以在开启 unstubEnvs 配置选项的情况下使用 vi.stubEnv 辅助函数(或在 beforeEach 钩子中手动调用 vi.unstubAllEnvs):

ts

import { expect, it, vi } from 'vitest'

// 运行测试前 "VITE_ENV" 为 "test" import.meta.env.VITE_ENV === 'test'

it('changes value', () => { vi.stubEnv('VITE_ENV', 'staging') expect(import.meta.env.VITE_ENV).toBe('staging') })

it('the value is restored before running an other test', () => { expect(import.meta.env.VITE_ENV).toBe('test') })

vitest.config.ts

export default defineConfig({ test: { unstubEnvs: true, }, })

评论 (0)