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() 等设置的实现。它不会恢复间谍的原始实现:
test.ts

恢复所有模拟

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

Vitest 兼容性

为了与为 Vitest 编写的测试增加兼容性,Bun 提供了 vi 对象作为 Jest 模拟 API 部分功能的别名:
test.ts
你可以在不重写模拟的情况下从 Vitest 移植测试。

实现细节

缓存交互

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

惰性求值

模拟工厂回调仅当模块被导入或 require 时才被求值。

路径解析

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,以便在运行时覆盖导出值并递归更新实时绑定。