Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a4a9640
test: support auto-mock for jest.mock(module) / jest.requireMock
robobun Apr 28, 2026
f3724cf
address coderabbit review
robobun Apr 28, 2026
c17337d
fix CI: update ENG-24434 test + include symbol-keyed properties
robobun Apr 28, 2026
d9017bb
address coderabbit: seed visited with mockProto, isolate parity tests
robobun Apr 28, 2026
33c473d
address claude: cache on-demand requireMock + remove dead topExceptio…
robobun Apr 28, 2026
cd5a969
test: fold auto-mock tests into mock-module.test.ts
robobun Apr 29, 2026
0e79a8f
address review: preserve attrs, isolate requireMock cache, fix re-mock
robobun Apr 29, 2026
6e463a4
fix: drop redundant JSMap has() call in auto-mock
robobun Apr 29, 2026
17ec124
address review: stash-and-restore, clear cache, index-key safe put
robobun Apr 29, 2026
4d97c19
fix(auto-mock): preserve primary exception when restoring stashed req…
robobun Apr 29, 2026
266397c
chore: retry CI (prior 3 failures are pre-existing Windows/ASAN flakes)
robobun Apr 29, 2026
cc2150a
fix(auto-mock): declare restoreStash before requireMap stash guards
robobun Apr 29, 2026
f38d5ed
address review: init stashedRequireMapEntry + align jest.mock return …
robobun Apr 29, 2026
a1649b7
chore: retry CI (ssg-pages-router is a known bake-dev HMR flake)
robobun Apr 29, 2026
7ba8617
chore: retry gate (prior runs had transient mimalloc download failures)
robobun Apr 29, 2026
9ad46ce
chore: retry CI+gate (prior runs hit transient network fetch failures…
robobun Apr 29, 2026
e6cc1e8
fix(auto-mock): re-seat stashed virtualModules entry before block exit
robobun Apr 29, 2026
ce6c1cd
chore: retry CI (astro-post.test.js Windows segfault is documented fl…
robobun Apr 29, 2026
6e65d2f
chore: retry CI (bun-create.test.ts hit GitHub API rate limit on Wind…
robobun Apr 29, 2026
a91e0aa
chore: retry CI
robobun Apr 30, 2026
6a960a6
port requireMock wiring to Rust jest.rs (mirrors jest.zig)
robobun May 14, 2026
ba2e33a
fix(auto-mock): set mockProto.constructor = mockFn back-reference
robobun May 14, 2026
51aec5f
ci: retrigger (fetch-tcp-keepalive flake on brand-new b8ecc78b03 code…
robobun May 14, 2026
caa78be
fix(auto-mock): guard virtualModules re-seat with hasVirtualModules()
robobun May 14, 2026
3e8921e
refactor(auto-mock): extract shared resolveModuleMockSpecifier helper
robobun Jun 5, 2026
57781ad
fix(auto-mock): don't set mustDoExpensiveRelativeLookup in requireMoc…
robobun Jun 5, 2026
72f49f1
fix: include JSMap.h in JSMockFunction.h for Strong<JSMap> instantiation
robobun Jul 9, 2026
ab81853
fix(auto-mock): reset expensive-lookup flag on failure; don't re-eval…
robobun Jul 9, 2026
08fccdf
Rework auto-mock walker to match jest-mock, add construct path, fix m…
robobun Aug 13, 2026
8540d3b
Condense comments in the mock install and auto-mock walker blocks
robobun Aug 13, 2026
4bc4154
Keep auto-mocks out of the ESM interop unwrap, stop the walk at the m…
robobun Aug 13, 2026
f13e021
Reset mustDoExpensiveRelativeLookup in Bun.plugin.clearAll()
robobun Aug 13, 2026
6670956
Don't spin on a pending promise when patching an already-required mock
robobun Aug 17, 2026
d4abbfa
Share the promise-unwrap helper, make spawned tests concurrent
robobun Aug 17, 2026
8dd0c61
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 17, 2026
b81ecb8
Give mocks a default prototype and mark unwrapped rejections handled
robobun Aug 17, 2026
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
49 changes: 45 additions & 4 deletions docs/test/mocks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,47 @@ test("mock.module", async () => {

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

### Auto-mocking

Calling `jest.mock(specifier)` (or `mock.module(specifier)`) without a factory auto-mocks the module: the real module is loaded and a mocked copy of its exports is generated. Functions become `jest.fn()` stubs that return `undefined`, classes become mock constructors whose static and prototype methods (including inherited ones) are mocked, nested plain objects are mocked recursively, and primitives, arrays and other exotic objects are preserved.

```ts title="test.ts" icon="/icons/typescript.svg"
import { test, expect, jest } from "bun:test";

jest.mock("./api-client");

test("auto-mocked module", () => {
const { fetchUser } = require("./api-client");

// fetchUser is a mock function with the real one's name and length.
expect(fetchUser("user-1")).toBeUndefined();
expect(fetchUser).toHaveBeenCalledWith("user-1");

// Configure it like any other mock.
fetchUser.mockReturnValue({ id: "user-1" });
});
```

Because the real module is loaded to generate the mock, its top-level side effects run. The `__mocks__` directory convention is not supported.

### jest.requireMock()

`jest.requireMock(specifier)` returns the mocked exports of a module. If the module was mocked with `jest.mock(specifier)`, the registered mock is returned. Otherwise an auto-mock is generated on demand and cached, without changing what `import` or `require` return for that module.

```ts title="test.ts" icon="/icons/typescript.svg"
import { test, expect, jest } from "bun:test";

test("requireMock", () => {
const mocked = jest.requireMock("./api-client") as any;
mocked.fetchUser.mockReturnValue({ id: "user-1" });
expect(mocked.fetchUser("user-1")).toEqual({ id: "user-1" });

// The real module is untouched.
const real = require("./api-client");
expect(real.fetchUser).not.toBe(mocked.fetchUser);
});
```

### Overriding Already Imported Modules

Calling `mock.module()` overrides the module even if it has already been imported.
Expand Down Expand Up @@ -516,10 +557,10 @@ Bun resolves the module specifier the same way it resolves an `import`, supporti

### Import Timing Effects

- **When mocking before first import**: No side effects from the original module occur
- **When mocking before first import**: With a factory, no side effects from the original module occur. The no-factory form (auto-mocking) loads the real module to generate the mock, so its side effects do run.
- **When mocking after import**: The original module's side effects have already happened

For this reason, use `--preload` for mocks that need to prevent side effects.
For this reason, use `--preload` with a factory for mocks that need to prevent side effects.

### Live Bindings

Expand Down Expand Up @@ -644,9 +685,9 @@ test("service calls API correctly", async () => {

## Notes

### Auto-mocking
### `__mocks__` directory

Bun does not support the `__mocks__` directory or auto-mocking. If this is blocking you from switching to Bun, [file an issue](https://github.com/oven-sh/bun/issues).
`jest.mock(specifier)` without a factory generates an auto-mock from the real module's exports (see [Auto-mocking](#auto-mocking) above), but the `__mocks__` directory convention is not supported. If this is blocking you from switching to Bun, [file an issue](https://github.com/oven-sh/bun/issues).

### ESM vs CommonJS

Expand Down
23 changes: 23 additions & 0 deletions packages/bun-types/test.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ declare module "bun:test" {
* ```
*/
module(id: string, factory: () => any): void | Promise<void>;
/**
* Auto-mock a module. Every function exported from `id` is replaced with a
* mock function that returns `undefined`, classes become mock constructors,
* and nested objects are recursively auto-mocked. Primitive exports and
* other builtin objects (arrays, dates, regexps, etc.) are preserved.
*
* The real module is loaded synchronously when this is called so we can
* introspect its exports — passing a factory bypasses that.
*/
module(id: string): void | Promise<void>;
/**
* Restore the previous value of mocks.
*/
Expand Down Expand Up @@ -92,6 +102,19 @@ declare module "bun:test" {
function clearAllMocks(): void;
function resetAllMocks(): void;
function fn<T extends (...args: any[]) => any>(func?: T): Mock<T>;
/**
* Register a module mock. With a factory, calls to `import`/`require`
* for `id` return the factory's return value. Without a factory, the
* real module is loaded and an auto-mock is synthesised — functions
* become `jest.fn()` stubs, classes become mock constructors, and
* nested objects are recursively auto-mocked.
*/
function mock(id: string, factory?: () => any): void | Promise<void>;
/**
* Return the mocked exports of a module. If `jest.mock(id)` hasn't been
* called yet, the module is loaded and auto-mocked on demand.
*/
function requireMock<T = unknown>(id: string): T;
Comment thread
robobun marked this conversation as resolved.
function setSystemTime(now?: number | Date): void;
function setTimeout(milliseconds: number): void;
function useFakeTimers(options?: { now?: number | Date } | "modern" | "legacy"): typeof vi;
Expand Down
Loading
Loading