Mocking in Jest
What is Mocking?
When unit testing, you want to test one thing in isolation. But real code depends on other things — databases, HTTP APIs, file systems, timers. Mocking replaces those dependencies with controlled fakes so your tests are:
- Fast — no real network calls
- Deterministic — same result every run
- Isolated — failures point to the code under test, not dependencies
jest.fn() — Mock Functions
The simplest mock: a function that records how it was called.
const mockFn = jest.fn();
mockFn("hello", 42);
mockFn("world");
expect(mockFn).toHaveBeenCalledTimes(2);
expect(mockFn).toHaveBeenCalledWith("hello", 42);
expect(mockFn).toHaveBeenLastCalledWith("world");
Controlling Return Values
const mockFn = jest.fn();
mockFn.mockReturnValue(42); // always returns 42
mockFn.mockReturnValueOnce("first")
.mockReturnValueOnce("second"); // different each call
// For async functions
mockFn.mockResolvedValue({ id: 1, name: "Rizwan" });
mockFn.mockRejectedValue(new Error("DB error"));
Practical Example
function sendWelcomeEmail(user, mailer) {
if (!user.email) throw new Error("User has no email");
return mailer.send({
to: user.email,
subject: "Welcome!",
body: `Hi ${user.name}, welcome aboard!`,
});
}
module.exports = { sendWelcomeEmail };
const { sendWelcomeEmail } = require("./emailService");
test("sends welcome email with correct data", async () => {
const mockMailer = { send: jest.fn().mockResolvedValue({ success: true }) };
const user = { name: "Rizwan", email: "rizwan@example.com" };
await sendWelcomeEmail(user, mockMailer);
expect(mockMailer.send).toHaveBeenCalledWith({
to: "rizwan@example.com",
subject: "Welcome!",
body: "Hi Rizwan, welcome aboard!",
});
});
test("throws if user has no email", () => {
const mockMailer = { send: jest.fn() };
expect(() => sendWelcomeEmail({ name: "Rizwan" }, mockMailer)).toThrow("User has no email");
expect(mockMailer.send).not.toHaveBeenCalled();
});
jest.mock() — Mocking Entire Modules
Mock an entire module so every function in it is a jest.fn():
const userService = require("./userService");
const { getUser } = require("./userController");
jest.mock("./userService"); // replaces all exports with jest.fn()
test("returns user from service", async () => {
userService.findById.mockResolvedValue({ id: 1, name: "Rizwan" });
const result = await getUser({ params: { id: 1 } });
expect(userService.findById).toHaveBeenCalledWith(1);
expect(result).toEqual({ id: 1, name: "Rizwan" });
});
Mocking Third-Party Libraries
jest.mock("axios");
const axios = require("axios");
test("fetches data from API", async () => {
axios.get.mockResolvedValue({
data: { posts: [{ id: 1, title: "Hello" }] },
});
const posts = await fetchPosts();
expect(axios.get).toHaveBeenCalledWith("https://api.example.com/posts");
expect(posts).toHaveLength(1);
});
Partial Mocks with jest.spyOn
Mock just one method of a module while keeping the rest real:
const fs = require("fs");
const { readConfig } = require("./config");
test("reads config file", () => {
const spy = jest.spyOn(fs, "readFileSync").mockReturnValue('{"port": 3000}');
const config = readConfig("./config.json");
expect(config.port).toBe(3000);
spy.mockRestore(); // restore original after test
});
Mocking ES Modules (with Babel)
For ES module syntax:
// __mocks__/axios.js ← manual mock file
const axios = {
get: jest.fn(),
post: jest.fn(),
};
export default axios;
Or with jest.mock() in the test file — Jest hoists the call above imports automatically:
import axios from "axios";
import { fetchUser } from "./api";
jest.mock("axios");
test("fetches user", async () => {
axios.get.mockResolvedValue({ data: { name: "Rizwan" } });
const user = await fetchUser(1);
expect(user.name).toBe("Rizwan");
});
Manual Mocks with __mocks__/
For complex modules you always want to mock, create a __mocks__ folder next to the module:
src/
database.js
__mocks__/
database.js ← this is used automatically when jest.mock('./database') is called
const mockDb = {
connect: jest.fn().mockResolvedValue(true),
disconnect: jest.fn().mockResolvedValue(true),
query: jest.fn().mockResolvedValue([]),
create: jest.fn((data) => Promise.resolve({ id: Date.now(), ...data })),
};
module.exports = mockDb;
In tests, just call jest.mock('./database') and the manual mock is used.
Mocking Timers
Replace setTimeout, setInterval, and Date.now with controllable fakes:
jest.useFakeTimers();
test("calls callback after delay", () => {
const callback = jest.fn();
setTimeout(callback, 1000);
expect(callback).not.toHaveBeenCalled();
jest.advanceTimersByTime(1000); // fast-forward 1 second
expect(callback).toHaveBeenCalledTimes(1);
});
afterEach(() => jest.useRealTimers()); // restore real timers
Mocking Date:
test("uses current date", () => {
jest.setSystemTime(new Date("2024-01-15"));
const result = getFormattedDate();
expect(result).toBe("2024-01-15");
jest.useRealTimers();
});
Clearing Mocks Between Tests
Mock state accumulates across tests unless you clear it. Three levels:
afterEach(() => {
jest.clearAllMocks(); // clears call history and return values
// jest.resetAllMocks(); // also removes mockReturnValue implementations
// jest.restoreAllMocks(); // also restores spies to original implementations
});
Or configure globally in jest.config.js:
module.exports = {
clearMocks: true, // clears mock.calls and mock.instances between tests
resetMocks: false,
restoreAllMocks: false,
};
When NOT to Mock
Avoid mocking things you own. If you mock UserService to test UserController, you're testing that your mock works, not that the controller and service work together. Use mocks for:
- Third-party services (email, payment, analytics)
- External HTTP APIs
- File system and environment
- Timers and random values
For your own code's integration points, write integration tests that use the real implementations.