Create a mock function with mock()
Import `mock` from `bun:test` and pass a function to create a mock. The mock replaces the function with a controlled implementation that tracks calls and results. Example: `const random = mock(() => Math.random());`
jest.fn() behaves identically to mock()
Bun supports `jest.fn()` for compatibility with Jest. It behaves identically to the `mock()` function and can be used interchangeably.
Mock function properties and methods
Mock functions returned by `mock()` have these properties and methods:
| Property/Method | Description |
|---|---|
| `mockFn.getMockName()` | Returns the mock name |
| `mockFn.mock.calls` | Array of call arguments for each invocation |
| `mockFn.mock.results` | Array of return values for each invocation |
| `mockFn.mock.instances` | Array of instances created with `new` |
| `mockFn.mock.contexts` | Array of `this` contexts for each invocation |
| `mockFn.mock.lastCall` | Arguments of the most recent call |
| `mockFn.mockClear()` | Clears call history |
| `mockFn.mockReset()` | Clears call history and removes implementation |
| `mockFn.mockRestore()` | Restores original implementation |
| `mockFn.mockImplementation(fn)` | Sets a new implementation |
| `mockFn.mockImplementationOnce(fn)` | Sets implementation for next call only |
| `mockFn.mockName(name)` | Sets the mock name |
| `mockFn.mockReturnThis()` | Sets the return value to `this` |
| `mockFn.mockReturnValue(value)` | Sets a return value |
| `mockFn.mockReturnValueOnce(value)` | Sets return value for next call only |
| `mockFn.mockResolvedValue(value)` | Sets a resolved Promise value |
| `mockFn.mockResolvedValueOnce(value)` | Sets resolved Promise for next call only |
| `mockFn.mockRejectedValue(value)` | Sets a rejected Promise value |
| `mockFn.mockRejectedValueOnce(value)` | Sets rejected Promise for next call only |
| `mockFn.withImplementation(fn, callback)` | Temporarily changes implementation |
Use spyOn() to spy without replacing
The `spyOn()` function tracks calls to a function without replacing it with a mock. It preserves the original implementation while allowing call verification. Import from `bun:test`. Syntax: `spyOn(object, 'methodName')`
Spy on service methods example
This example demonstrates spying on methods to verify they are called correctly while preserving original implementation:
```ts
import { test, expect, spyOn, afterEach } from "bun:test";
class UserService {
async getUser(id: string) {
return { id, name: `User ${id}` };
}
async saveUser(user: any) {
return { ...user, saved: true };
}
}
const userService = new UserService();
afterEach(() => {
jest.restoreAllMocks();
});
test("spy on service methods", async () => {
const getUserSpy = spyOn(userService, "getUser");
const saveUserSpy = spyOn(userService, "saveUser");
const user = await userService.getUser("123");
await userService.saveUser(user);
expect(getUserSpy).toHaveBeenCalledWith("123");
expect(saveUserSpy).toHaveBeenCalledWith(user);
});
test("spy with mock implementation", async () => {
const getUserSpy = spyOn(userService, "getUser").mockResolvedValue({
id: "123",
name: "Mocked User",
});
const result = await userService.getUser("123");
expect(result.name).toBe("Mocked User");
expect(getUserSpy).toHaveBeenCalledWith("123");
});
```
mock.module() to override a module
Use `mock.module(path: string, callback: () => Object)` to override the behavior of a module. The callback function returns an object with the mocked exports. Supports both ESM `import` and CommonJS `require`.
mock.module() basic example
This example shows how to mock a module:
```ts
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");
});
```
mock.module() updates live bindings
Calling `mock.module()` overrides the module even if it has already been imported. Live bindings are updated, so previously imported references reflect the new mocked values for both ESM and CommonJS.
Use --preload to mock before imports
To ensure a module is mocked before it is imported, preventing the original module from being evaluated and its side effects from running, use the `--preload` flag when running tests: `bun test --preload ./my-preload`. To avoid typing this every time, add it to `bunfig.toml` under `[test]` section as `preload = ["./my-preload"]`.
mock.clearAllMocks() resets call history only
`mock.clearAllMocks()` resets the `.mock.calls`, `.mock.instances`, `.mock.contexts`, and `.mock.results` properties of every mock. Unlike `mock.restore()`, it does not restore the original implementation. The mock implementations set by `mockImplementation()`, `mockReturnValue()`, etc. are preserved.
jest.resetAllMocks() drops implementations
`jest.resetAllMocks()` (alias `vi.resetAllMocks()`) calls `mockFn.mockReset()` on every mock. In addition to clearing call history like `clearAllMocks()` does, it also drops the implementations set by `mockImplementation()`, `mockReturnValue()`, and similar methods. It does not restore the original implementation of a spy.
mock.restore() restores all mocks
`mock.restore()` restores every mock at once instead of calling `mockFn.mockRestore()` individually. It restores the original implementations of function mocks and spies. It does not reset modules overridden with `mock.module()`. Call it in an `afterEach` block or in a preload script to avoid repeating cleanup in every test.
mock.restore() example with spies
This example demonstrates restoring all spies at once:
```ts
import { expect, mock, spyOn, test } from "bun:test";
import * as fooModule from "./foo.ts";
import * as barModule from "./bar.ts";
import * as bazModule from "./baz.ts";
test("foo, bar, baz", () => {
const fooSpy = spyOn(fooModule, "foo");
const barSpy = spyOn(barModule, "bar");
const bazSpy = spyOn(bazModule, "baz");
// Original implementations still work
expect(fooModule.foo()).toBe("foo");
expect(barModule.bar()).toBe("bar");
expect(bazModule.baz()).toBe("baz");
// Mock implementations
fooSpy.mockImplementation(() => 42);
barSpy.mockImplementation(() => 43);
bazSpy.mockImplementation(() => 44);
expect(fooModule.foo()).toBe(42);
expect(barModule.bar()).toBe(43);
expect(bazModule.baz()).toBe(44);
// Restore all
mock.restore();
expect(fooModule.foo()).toBe("foo");
expect(barModule.bar()).toBe("bar");
expect(bazModule.baz()).toBe("baz");
});
```
Vitest compatibility with vi object
For compatibility with tests written for Vitest, Bun provides the `vi` object as an alias for parts of the Jest mocking API. Available functions on `vi`: `vi.fn`, `vi.spyOn`, `vi.mock`, `vi.restoreAllMocks`, `vi.resetAllMocks`, `vi.clearAllMocks`. This allows porting Vitest tests without rewriting mocks.
Module mock path resolution
When mocking a module with `mock.module()`, Bun resolves the module specifier the same way it resolves an `import`, supporting relative paths (`'./module'`), absolute paths (`'/path/to/module'`), and package names (`'lodash'`).
Mock factory callback lazy evaluation
The mock factory callback passed to `mock.module()` is only evaluated when the module is imported or required, not when `mock.module()` is called.
Mocked ESM modules maintain live bindings
Mocked ESM modules maintain live bindings, so changing the mock updates all existing imports of that module.
Module mocks with ESM and CommonJS
Module mocks interact with both ESM and CommonJS module caches. For ES modules, Bun patches JavaScriptCore so it can override export values at runtime and update live bindings recursively.
Bun does not support auto-mocking
Bun does not support the `__mocks__` directory or auto-mocking pattern used in Jest. Mocks must be explicitly created with `mock()`, `spyOn()`, or `mock.module()`.
API client mock module example
This example shows mocking an API client module with multiple mock functions:
```ts
import { test, expect, mock, beforeEach } from "bun:test";
mock.module("./api-client", () => ({
fetchUser: mock(async (id: string) => ({ id, name: `User ${id}` })),
createUser: mock(async (user: any) => ({ ...user, id: "new-id" })),
updateUser: mock(async (id: string, user: any) => ({ ...user, id })),
}));
test("user service with mocked API", async () => {
const { fetchUser } = await import("./api-client");
const { UserService } = await import("./user-service");
const userService = new UserService();
const user = await userService.getUser("123");
expect(fetchUser).toHaveBeenCalledWith("123");
expect(user.name).toBe("User 123");
});
```
Mock external dependencies example
This example shows mocking an external database library:
```ts
import { test, expect, mock } from "bun:test";
mock.module("pg", () => ({
Client: mock(function () {
return {
connect: mock(async () => {}),
query: mock(async (sql: string) => ({
rows: [{ id: 1, name: "Test User" }],
})),
end: mock(async () => {}),
};
}),
}));
test("database operations", async () => {
const { Database } = await import("./database");
const db = new Database();
const users = await db.getUsers();
expect(users).toHaveLength(1);
expect(users[0].name).toBe("Test User");
});
```
Basic mock function example
This example demonstrates basic mock function usage with call verification:
```ts
import { test, expect, mock } from "bun:test";
test("mock function behavior", () => {
const mockFn = mock((x: number) => x * 2);
const result1 = mockFn(5);
const result2 = mockFn(10);
expect(mockFn).toHaveBeenCalledTimes(2);
expect(mockFn).toHaveBeenCalledWith(5);
expect(mockFn).toHaveBeenLastCalledWith(10);
expect(result1).toBe(10);
expect(result2).toBe(20);
expect(mockFn.mock.calls).toEqual([[5], [10]]);
expect(mockFn.mock.results).toEqual([
{ type: "return", value: 10 },
{ type: "return", value: 20 },
]);
});
```
Dynamic mock implementations example
This example shows setting different implementations for successive calls:
```ts
import { test, expect, mock } from "bun:test";
test("dynamic mock implementations", () => {
const mockFn = mock();
mockFn.mockImplementationOnce(() => "first");
mockFn.mockImplementationOnce(() => "second");
mockFn.mockImplementation(() => "default");
expect(mockFn()).toBe("first");
expect(mockFn()).toBe("second");
expect(mockFn()).toBe("default");
expect(mockFn()).toBe("default");
});
```
Async mock functions example
This example demonstrates mocking async functions with resolved and rejected values:
```ts
import { test, expect, mock } from "bun:test";
test("async mock functions", async () => {
const asyncMock = mock();
asyncMock.mockResolvedValueOnce("first result");
asyncMock.mockResolvedValue("default result");
expect(await asyncMock()).toBe("first result");
expect(await asyncMock()).toBe("default result");
const rejectMock = mock();
rejectMock.mockRejectedValue(new Error("Mock error"));
await expect(rejectMock()).rejects.toThrow("Mock error");
});
```
clear all mocks example
This example shows `mock.clearAllMocks()` resetting call history while preserving implementations:
```ts
import { expect, mock, test } from "bun:test";
const random1 = mock(() => Math.random());
const random2 = mock(() => Math.random());
test("clearing all mocks", () => {
random1();
random2();
expect(random1).toHaveBeenCalledTimes(1);
expect(random2).toHaveBeenCalledTimes(1);
mock.clearAllMocks();
expect(random1).toHaveBeenCalledTimes(0);
expect(random2).toHaveBeenCalledTimes(0);
expect(typeof random1()).toBe("number");
expect(typeof random2()).toBe("number");
});
```
reset all mocks example
This example shows `jest.resetAllMocks()` clearing call history and removing implementations:
```ts
import { expect, jest, test } from "bun:test";
const random = jest.fn(() => Math.random());
test("resetting all mocks", () => {
random();
expect(random).toHaveBeenCalledTimes(1);
jest.resetAllMocks();
expect(random).toHaveBeenCalledTimes(0);
expect(random()).toBeUndefined();
});
```