Skip to main content
模拟会使用受控的实现替换依赖项。Bun 支持函数模拟、间谍以及模块模拟。

基础函数模拟

使用 mock 函数创建模拟。
test.ts

Jest 兼容性

你也可以像在 Jest 中一样使用 jest.fn()。它的行为完全相同。
test.ts

模拟函数属性

mock() 返回一个带有附加属性的新函数。
test.ts

可用属性和方法

模拟函数实现了以下属性和方法:

实际示例

基础模拟用法

test.ts

动态模拟实现

test.ts

异步模拟

test.ts

使用 spyOn() 创建间谍

使用 spyOn() 跟踪对函数的调用,而不会将其替换为模拟函数。间谍可以传递给 .toHaveBeenCalled().toHaveBeenCalledTimes()
test.ts

高级间谍用法

test.ts

使用 mock.module() 进行模块模拟

使用 mock.module(path: string, callback: () => Object) 覆盖模块的行为。
test.ts
与 Bun 其他部分一样,模块模拟同时支持 importrequire

重写已导入模块

调用 mock.module() 会覆盖模块,即使该模块已经被导入。
test.ts

提前声明与预加载

为了确保模块在导入之前已被模拟,请使用 --preload 在测试运行前加载模拟模块。
my-preload.ts
terminal
为了避免每次运行测试时都输入 --preload,请将其添加到 bunfig.toml 中:
bunfig.toml

模块模拟最佳实践

何时使用预加载

模拟已经导入的模块会更新模块缓存,因此任何导入该模块的代码都会获取模拟版本。不过,原始模块已经执行过,因此其副作用也已经发生。 若要完全避免执行原始模块,请使用 --preload 在测试运行前加载模拟模块。

实际模块模拟示例

api-client.test.ts

模拟外部依赖

database.test.ts

全局模拟函数

清除所有模拟

mock.clearAllMocks() 会重置每个模拟的 .mock.calls.mock.instances.mock.contexts.mock.results 属性。与 mock.restore() 不同,它不会恢复原始实现:
test.ts

重置所有模拟

jest.resetAllMocks()(及其 vi.resetAllMocks() 别名)会对每个模拟调用 mockFn.mockReset():除了执行 clearAllMocks() 的操作外,它还会删除通过 mockImplementation()mockReturnValue() 等方法设置的实现。它不会恢复 spy 的原始实现:
test.ts

恢复所有模拟

mock.restore() 一次性恢复所有模拟,而不必分别对每个模拟调用 mockFn.mockRestore()。它不会重置通过 mock.module() 覆盖的模块。
test.ts
afterEach 块或测试预加载脚本中调用 mock.restore(),这样就不必在每个测试中重复清理操作。

Vitest 兼容性

为方便移植 Vitest 编写的测试,Bun 提供了 vi 对象作为 Jest 模拟 API 部分的别名:
test.ts
你可以直接移植 Vitest 测试,无需重写模拟函数。

实现细节

缓存交互

模块模拟会与 ESM 和 CommonJS 模块缓存交互。

惰性求值

模拟工厂回调仅在模块被导入或加载时求值。

路径解析

Bun 以解析 import 的相同方式解析模块说明符,支持:
  • 相对路径('./module'
  • 绝对路径('/path/to/module'
  • 包名('lodash'

导入时机影响

  • 在首次导入前模拟:原模块不会执行副作用
  • 在导入后模拟:原模块副作用已经发生
因此,对于需要阻止副作用的模拟,请使用 --preload

实时绑定

被模拟的 ESM 模块会维护实时绑定,因此更改模拟内容会更新所有现有导入。

高级模式

工厂函数

test.ts

条件式模拟

test.ts

模拟清理模式

test.ts

最佳实践

保持模拟简单

test.ts

使用类型安全的模拟

测试模拟行为

test.ts

备注

自动模拟

Bun 不支持 __mocks__ 目录或自动模拟。如果这阻碍了你迁移到 Bun,请提交 issue

ESM 与 CommonJS

ESM 和 CommonJS 模块的模块模拟实现方式不同。对于 ES 模块,Bun 会修补 JavaScriptCore,以便在运行时覆盖导出值,并递归更新实时绑定。