The Mock System provides Jest and Vitest-compatible function mocking and spying capabilities for Bun's test runner. It enables creating mock functions that record calls and allow controlling return values, simulating errors, and temporarily replacing object methods. This system is part of the Testing Framework (#6) and works in conjunction with the Expect API (#6.2) for assertions on mock behavior.
The mock system is implemented across three layers: C++ (JavaScriptCore bindings), Zig (FFI bridge), and JavaScript (public API).
Diagram: Mock System Architecture
Sources: src/bun.js/bindings/JSMockFunction.cpp1-1000 src/bun.js/bindings/JSMockFunction.h1-100 src/bun.js/test/jest.zig213-261
JSMockFunction is the C++ class representing a mock function, extending JSC::InternalFunction to behave as a callable JavaScript function.
| Property | Type | Purpose |
|---|---|---|
implementation | WriteBarrier<Unknown> | Head of implementation chain, executed next |
fallbackImplementation | WriteBarrier<Unknown> | Default non-once implementation |
tail | WriteBarrier<Unknown> | Last once implementation in chain |
spyOriginal | WriteBarrier<Unknown> | Original function for spy restoration |
calls | WriteBarrier<JSArray> | Array of argument arrays for each call |
contexts | WriteBarrier<JSArray> | Array of this values for each call |
invocationCallOrder | WriteBarrier<JSArray> | Global invocation order numbers |
instances | WriteBarrier<JSArray> | Array of constructed instances (for constructors) |
returnValues | WriteBarrier<JSArray> | Array of return values from each call |
spyTarget | Weak<JSObject> | Weak reference to spied object |
spyIdentifier | Identifier | Property name being spied on |
spyAttributes | unsigned | Original property attributes |
mock | LazyProperty<JSObject> | Lazy-initialized mock metadata object |
Sources: src/bun.js/bindings/JSMockFunction.cpp231-294
The mock lazy property provides access to call tracking data via getters:
Diagram: Mock Metadata Object Structure
Sources: src/bun.js/bindings/JSMockFunction.cpp432-573
Mock functions are created through jest.fn() (aliased as mock() and vi.fn()):
Diagram: Mock Function Creation Flow
The C++ implementation:
JSMockFunction with CallbackKind::CallSources: src/bun.js/bindings/JSMockFunction.cpp1193-1301 src/bun.js/test/jest.zig217
JSMockImplementation represents different implementation strategies for mock functions. It forms a linked list for "once" implementations.
Diagram: Implementation Kinds
| Kind | Stored Value | Behavior |
|---|---|---|
Call | Function to execute | Calls the function with arguments |
ReturnValue | Value to return | Returns the value (resolves if promise) |
ReturnThis | (unused) | Returns the this context |
RejectedValue | Error value | Rejects with the value |
Diagram: Implementation Chain
The nextValueOrSentinel field serves dual purpose:
undefined: No next value, not a once implementationjsNumber(1): No next value, is a once implementationJSMockImplementation: Next value, is a once implementationSources: src/bun.js/bindings/JSMockFunction.cpp144-209
When a mock function is called, the system:
Diagram: Mock Function Invocation Flow
Sources: src/bun.js/bindings/JSMockFunction.cpp574-803
Spying allows intercepting calls to existing object methods while preserving the original behavior.
Diagram: spyOn Flow
Key aspects:
spyOriginal fieldspyAttributes includes SpyAttributeESModuleNamespace flag for ES module namespace spyingSources: src/bun.js/bindings/JSMockFunction.cpp1303-1553
mockRestore() reverses the spy operation:
spyTarget)putDirect() to bypass readonlyActiveSpySetSources: src/bun.js/bindings/JSMockFunction.cpp1050-1120
Diagram: Implementation Setting Flow
Sources: src/bun.js/bindings/JSMockFunction.cpp900-1000
These methods create implementations with specific return behaviors:
| Method | Implementation Kind | Behavior |
|---|---|---|
mockReturnValue(val) | ReturnValue | Returns val (resolves if promise) |
mockReturnValueOnce(val) | ReturnValue (once) | Returns val once |
mockResolvedValue(val) | ReturnValue | Returns Promise.resolve(val) |
mockResolvedValueOnce(val) | ReturnValue (once) | Returns Promise.resolve(val) once |
mockRejectedValue(err) | RejectedValue | Returns Promise.reject(err) |
mockRejectedValueOnce(err) | RejectedValue (once) | Returns Promise.reject(err) once |
mockReturnThis() | ReturnThis | Returns the this context |
Sources: src/bun.js/bindings/JSMockFunction.cpp1001-1150
Diagram: Clear vs Reset Behavior
mockClear(): Clears call history but preserves implementationsmockReset(): Clears call history and removes all implementationsSources: src/bun.js/bindings/JSMockFunction.cpp804-875
JSMockModule manages global state for the mock system within a JSGlobalObject:
| Property | Type | Purpose |
|---|---|---|
s_nextInvocationId | static uint64_t | Global counter for call ordering |
mockFunctionStructure | LazyProperty<Structure> | Cached structure for JSMockFunction |
mockResultStructure | LazyProperty<Structure> | Cached structure for mock results |
mockImplementationStructure | LazyProperty<Structure> | Cached structure for implementations |
mockObjectStructure | LazyProperty<Structure> | Cached structure for mock metadata |
activeSpySetStructure | LazyProperty<Structure> | Cached structure for spy tracking |
withImplementationCleanupFunction | LazyProperty<JSFunction> | Cleanup function for temporary implementations |
Sources: src/bun.js/bindings/JSMockFunction.h18-50 src/bun.js/bindings/JSMockFunction.cpp110
ActiveSpySet is a WeakSet-like structure that tracks active spies:
Diagram: ActiveSpySet Management
The set uses weak references, so spies are automatically removed when their mock functions are garbage collected.
Sources: src/bun.js/bindings/JSMockFunction.cpp116-143 src/bun.js/bindings/JSMockFunction.cpp1555-1599
jest.clearAllMocks():
ActiveSpySetmockClear() on each spyjest.restoreAllMocks():
ActiveSpySetmockRestore() on each spySources: src/bun.js/bindings/JSMockFunction.cpp1601-1678 src/bun.js/test/jest.zig219-220
withImplementation(fn, callback) temporarily changes a mock's implementation:
Diagram: withImplementation Flow
The cleanup is scheduled using the withImplementationCleanupFunction which ensures restoration happens even if the callback throws.
Sources: src/bun.js/bindings/JSMockFunction.cpp1151-1191
The TypeScript definitions provide the public API surface:
Diagram: Mock Type Structure
Sources: packages/bun-types/test.d.ts17-201
Refresh this wiki