Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/test/mocks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,8 @@ test("mock.module", async () => {

Like the rest of Bun, module mocks support both `import` and `require`.

`require()` can't wait for a promise. Before any `import` of the module, `require()` doesn't call an `async` factory that uses `await`. It returns the original module instead, as in Vitest. To get the mock from `require()`, import the module first. The `import` calls the factory, and `require()` returns the mock once the factory's promise is fulfilled.

### Overriding Already Imported Modules

Calling `mock.module()` overrides the module even if it has already been imported.
Expand Down
40 changes: 40 additions & 0 deletions test/js/bun/test/mock/mock-module.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,46 @@ describe.concurrent("the original of a mocked module", () => {
},
);

test.each(["mock.module", "vi.mock", "jest.mock"])(
"before any import of the module, require() returns the original and does not call an async factory of %s() that awaits",
async mockModule => {
await expectFixturesToPass(
{
...modules,
"mod.cjs": `exports.value = "real";`,
"waits.test.ts": `
import { expect, jest, mock, test, vi } from "bun:test";
const state = vi.hoisted(() => ({ calls: [], rejects: 0 }));
${mockModule}("./mod", async () => (state.calls.push("./mod"), await 1, { value: "mocked" }));
${mockModule}("./mod.cjs", async () => (state.calls.push("./mod.cjs"), await 1, { value: "mocked" }));
${mockModule}("cjs-pkg", async () => (state.calls.push("cjs-pkg"), await 1, { hello: () => "mocked" }));
${mockModule}("node:os", async () => (state.calls.push("node:os"), await 1, { platform: () => "mocked" }));
${mockModule}("./sub/thing", async () => {
await 1;
if (++state.rejects === 1) throw new Error("rejected");
return { thing: "mocked" };
});
const required = () => [require("./mod").value, require("./mod.cjs").value, require("cjs-pkg").hello(), require("node:os").platform()];
test("a file, a CommonJS file, a package and a builtin", async () => {
const originals = ["real", "real", "real-hello", process.platform];
expect([required(), required(), state.calls]).toEqual([originals, originals, []]);
const imported = [await import("./mod"), await import("./mod.cjs"), await import("cjs-pkg"), await import("node:os")];
expect([imported[0].value, imported[1].value, imported[2].hello(), imported[3].platform()]).toEqual(Array(4).fill("mocked"));
expect([required(), state.calls]).toEqual([Array(4).fill("mocked"), ["./mod", "./mod.cjs", "cjs-pkg", "node:os"]]);
});
test("and after an import whose factory rejected, until one is fulfilled", async () => {
expect([require("./sub/thing").thing, state.rejects]).toEqual(["real-thing", 0]);
await expect(import("./sub/thing")).rejects.toThrow("rejected");
expect([require("./sub/thing").thing, state.rejects]).toEqual(["real-thing", 1]);
expect([(await import("./sub/thing")).thing, require("./sub/thing").thing, state.rejects]).toEqual(["mocked", "mocked", 2]);
});
`,
},
2,
);
},
);

test("jest.requireActual", async () => {
await expectFixturesToPass(
{
Expand Down
Loading