Spies & Tracking
Spy on methods with jest.spyOn, track calls, and override implementations temporarily.
jest.spyOn Basics
jest.spyOn(object, "methodName") wraps real implementation by default while recording calls. Pass mock implementation as third argument to replace behavior temporarily.
Spies restore original with mockRestore()—call in afterEach to prevent leaking mocks across tests.
const consoleSpy = jest.spyOn(console, "error").mockImplementation(() => {});
logError("fail");
expect(consoleSpy).toHaveBeenCalledWith("fail");
consoleSpy.mockRestore();Call Tracking
Matchers include toHaveBeenCalled, toHaveBeenCalledTimes, toHaveBeenCalledWith, and toHaveBeenLastCalledWith. Access mock.calls and mock.results arrays for detailed inspection.
Use toHaveBeenNthCalledWith for ordered multi-call assertions in retry or batch scenarios.
const fn = jest.fn();
fn("a");
fn("b");
expect(fn).toHaveBeenCalledTimes(2);
expect(fn.mock.calls[0]).toEqual(["a"]);Mock Return Values
mockReturnValueOnce chains sequential return values for repeated calls. mockResolvedValueOnce and mockRejectedValueOnce handle async sequences.
Default mockReturnValue applies to all calls after once chain exhausted unless reset.
- mockClear resets call history but keeps implementation
- mockReset clears history and removes implementation
- Prefer restore over reset when done spying on real methods
const random = jest.spyOn(Math, "random") .mockReturnValueOnce(0.1) .mockReturnValueOnce(0.9);
Mock Implementations
mockImplementation receives call arguments and returns computed values—useful for simulating stateful dependencies. mockImplementationOnce applies per call.
Delegate to original with spy and partial override: jest.spyOn(obj, "m").mockImplementation((...args) => original(...args) + 1).
const spy = jest.spyOn(api, "getUser").mockImplementation(async (id) => ({
id,
name: "Test User",
}));Spies vs Module Mocks
Spies work on existing object methods—great for console, timers, and imported namespace objects. Module mocks replace entire modules at import graph level.
Combine both carefully: spy on method of default export after jest.mock provides mock module object.
- Avoid spying on production singletons without restore in afterEach
- Track side effects via spies on collaborators, not internal private methods
- Use jest.spyOn(Date, "now") with fake timers for time-dependent code