使用 mock
函數建立模擬函數。
import { test, expect, mock } from "bun:test";
const random = mock(() => Math.random());
test("random", async () => {
const val = random();
expect(val).toBeGreaterThan(0);
expect(random).toHaveBeenCalled();
expect(random).toHaveBeenCalledTimes(1);
});
或者,您可以使用 jest.fn()
函數,就像在 Jest 中一樣。它的行為完全相同。
import { test, expect, jest } from "bun:test";
const random = jest.fn(() => Math.random());
test("random", async () => {
const val = random();
expect(val).toBeGreaterThan(0);
expect(random).toHaveBeenCalled();
expect(random).toHaveBeenCalledTimes(1);
});
mock()
的結果是一個新的函數,它已使用一些附加屬性進行裝飾。
import { mock } from "bun:test";
const random = mock((multiplier: number) => multiplier * Math.random());
random(2);
random(10);
random.mock.calls;
// [[ 2 ], [ 10 ]]
random.mock.results;
// [
// { type: "return", value: 0.6533907460954099 },
// { type: "return", value: 0.6452713933037312 }
// ]
模擬函數上實作了以下屬性和方法。
.spyOn()
可以追蹤函數的呼叫,而不用用模擬函數取代它。使用 spyOn()
建立一個間諜;這些間諜可以傳遞給 .toHaveBeenCalled()
和 .toHaveBeenCalledTimes()
。
import { test, expect, spyOn } from "bun:test";
const ringo = {
name: "Ringo",
sayHi() {
console.log(`Hello I'm ${this.name}`);
},
};
const spy = spyOn(ringo, "sayHi");
test("spyon", () => {
expect(spy).toHaveBeenCalledTimes(0);
ringo.sayHi();
expect(spy).toHaveBeenCalledTimes(1);
});
使用 mock.module()
的模組模擬函數
模組模擬可讓您覆寫模組的行為。使用 mock.module(path: string, callback: () => Object)
來模擬模組。
import { test, expect, mock } from "bun:test";
mock.module("./module", () => {
return {
foo: "bar",
};
});
test("mock.module", async () => {
const esm = await import("./module");
expect(esm.foo).toBe("bar");
const cjs = require("./module");
expect(cjs.foo).toBe("bar");
});
與 Bun 的其他部分一樣,模組模擬同時支援 import
和 require
。
覆寫已匯入的模組
如果您需要覆寫已匯入的模組,您不需要執行任何特殊操作。只要呼叫 mock.module()
,模組就會被覆寫。
import { test, expect, mock } from "bun:test";
// The module we're going to mock is here:
import { foo } from "./module";
test("mock.module", async () => {
const cjs = require("./module");
expect(foo).toBe("bar");
expect(cjs.foo).toBe("bar");
// We update it here:
mock.module("./module", () => {
return {
foo: "baz",
};
});
// And the live bindings are updated.
expect(foo).toBe("baz");
// The module is also updated for CJS.
expect(cjs.foo).toBe("baz");
});
提升和預載入
如果您需要確保在匯入模組之前模擬它,您應該使用 --preload
在測試執行之前載入您的模擬。
// my-preload.ts
import { mock } from "bun:test";
mock.module("./module", () => {
return {
foo: "bar",
};
});
bun test --preload ./my-preload
為了讓您的工作更輕鬆,您可以在 bunfig.toml
中放置 preload
[test]
# Load these modules before running tests.
preload = ["./my-preload"]
如果我模擬已匯入的模組,會發生什麼事?
如果您模擬已匯入的模組,模組會在模組快取中更新。這表示任何匯入該模組的模組都會取得模擬的版本,但原始模組仍會被評估。這表示原始模組的任何副作用仍會發生。
如果您想防止評估原始模組,您應該使用 --preload
在測試執行之前載入您的模擬。
__mocks__
目錄和自動模擬
尚未支援自動模擬。如果這阻礙您切換到 Bun,請提交問題。
實作細節
模組模擬對 ESM 和 CommonJS 模組有不同的實作。對於 ES 模組,我們已新增 JavaScriptCore 的修補程式,讓 Bun 能在執行階段覆寫匯出值並遞迴更新動態繫結。
自 Bun v1.0.19 起,Bun 會自動將 mock.module()
的 specifier
參數解析為您執行 import
的結果。如果解析成功,則解析後的 specifier 字串會用作模組快取中的金鑰。這表示您可以使用相對路徑、絕對路徑,甚至是模組名稱。如果 specifier
無法解析,則原始 specifier
會用作模組快取中的金鑰。
解析後,模擬的模組會儲存在 ES 模組登錄檔和 CommonJS require 快取中。這表示您可以對模擬的模組交換使用 import
和 require
。
只有在匯入或需要模組時,才會呼叫回呼函式。這表示您可以使用 mock.module()
模擬尚未存在的模組,而且表示您可以使用 mock.module()
模擬由其他模組匯入的模組。