Skip to main content

Jest Cheatsheet

Comprehensive quick reference for Jest testing framework including matchers, mocking, async testing, configuration, and best practices for JavaScript/TypeScript projects.

Table of Contents


Prerequisites

Jest Version: This cheatsheet targets Jest 29+. Some features require specific versions (noted inline).

Node.js: Jest 29+ requires Node.js 14.17+ or Node.js 16+ for optimal performance.

TypeScript Support: Use @types/jest and ts-jest or ts-node for TypeScript projects.


Installation & Setup

Initial Setup 🔧

Terminal window
# Install Jest and TypeScript types
pnpm add -D jest @types/jest
# Install TypeScript support (optional)
pnpm add -D ts-jest typescript
# Initialize Jest config
npx jest --init
# → Creates jest.config.js with interactive prompts
# Run tests
pnpm test
# or
pnpm jest
# Run tests in watch mode
pnpm test --watch
# or
pnpm jest --watch
# Run tests with coverage
pnpm test --coverage
# or
pnpm jest --coverage

Package.json Scripts 📝

{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test:ci": "jest --ci --coverage --maxWorkers=2"
}
}

Basic Configuration ⚙️

jest.config.js
module.exports = {
// Test environment
testEnvironment: "node", // 'node' | 'jsdom' | 'jest-environment-jsdom'
// File extensions to test
moduleFileExtensions: ["js", "jsx", "ts", "tsx", "json"],
// Transform files
transform: {
"^.+\\.tsx?$": "ts-jest",
"^.+\\.jsx?$": "babel-jest",
},
// Test match patterns
testMatch: ["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)"],
// Coverage settings
collectCoverageFrom: [
"src/**/*.{js,jsx,ts,tsx}",
"!src/**/*.d.ts",
"!src/**/*.stories.{js,jsx,ts,tsx}",
],
// Module name mapping
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/src/$1",
},
// Setup files
setupFilesAfterEnv: ["<rootDir>/src/setupTests.ts"],
};

Basic Testing

Test Structure 📋

// Basic test
test("adds 1 + 2 to equal 3", () => {
expect(1 + 2).toBe(3);
});
// Using describe blocks for grouping
describe("Math operations", () => {
test("addition", () => {
expect(2 + 2).toBe(4);
});
test("subtraction", () => {
expect(5 - 3).toBe(2);
});
// Nested describe blocks
describe("multiplication", () => {
test("positive numbers", () => {
expect(3 * 4).toBe(12);
});
test("negative numbers", () => {
expect(-2 * 3).toBe(-6);
});
});
});
// Using it() alias
it("should return true", () => {
expect(true).toBe(true);
});
// Test with async function
test("async test", async () => {
const result = await Promise.resolve(42);
expect(result).toBe(42);
});

Test Naming Conventions 📝

// ✅ Good: Descriptive test names
test("should return user object when valid ID is provided", () => {
// ...
});
test("throws error when user ID is invalid", () => {
// ...
});
// ❌ Bad: Vague test names
test("test user", () => {
// ...
});
test("works", () => {
// ...
});

Skipping Tests ⏭️

// Skip a test
test.skip("this test is skipped", () => {
expect(true).toBe(false);
});
// Skip with describe
describe.skip("skipped suite", () => {
test("this will not run", () => {
// ...
});
});
// Conditional skip
test("only runs in CI", () => {
if (!process.env.CI) {
test.skip("skipped locally");
}
// ...
});
// Run only this test
test.only("only this test runs", () => {
expect(true).toBe(true);
});

Matchers

Equality Matchers ⚖️

// toBe() - strict equality (===)
test("strict equality", () => {
expect(2 + 2).toBe(4);
expect("hello").toBe("hello");
expect({}).not.toBe({}); // Different object references
});
// toEqual() - deep equality
test("deep equality", () => {
expect({ a: 1, b: 2 }).toEqual({ a: 1, b: 2 });
expect([1, 2, 3]).toEqual([1, 2, 3]);
const obj = { name: "John" };
expect(obj).toEqual({ name: "John" });
});
// toStrictEqual() - strict deep equality (checks undefined, type)
test("strict deep equality", () => {
expect({ a: 1 }).toStrictEqual({ a: 1 });
expect({ a: 1, b: undefined }).not.toStrictEqual({ a: 1 });
});

Truthiness Matchers ✅

// toBeTruthy() / toBeFalsy()
test("truthiness", () => {
expect(true).toBeTruthy();
expect(1).toBeTruthy();
expect("hello").toBeTruthy();
expect({}).toBeTruthy();
expect(false).toBeFalsy();
expect(0).toBeFalsy();
expect("").toBeFalsy();
expect(null).toBeFalsy();
expect(undefined).toBeFalsy();
});
// toBeNull() / toBeUndefined() / toBeDefined()
test("null and undefined", () => {
expect(null).toBeNull();
expect(undefined).toBeUndefined();
expect("value").toBeDefined();
expect(null).toBeDefined(); // null is defined
});

Number Matchers 🔢

// toBeGreaterThan() / toBeGreaterThanOrEqual()
test("number comparisons", () => {
expect(10).toBeGreaterThan(5);
expect(10).toBeGreaterThanOrEqual(10);
expect(5).toBeLessThan(10);
expect(5).toBeLessThanOrEqual(5);
// Floating point equality
expect(0.1 + 0.2).toBeCloseTo(0.3, 5);
expect(0.1 + 0.2).not.toBe(0.3); // Floating point precision issue
});

String Matchers 📝

// toMatch() - regex or substring
test("string matching", () => {
expect("Hello World").toMatch("World");
expect("Hello World").toMatch(/World/);
expect("Hello World").toMatch(/^Hello/);
expect("test@example.com").toMatch(/^[\w-]+@[\w-]+\.[\w-]+$/);
// Case insensitive
expect("Hello").toMatch(/hello/i);
});
// toContain() - substring or array element
test("containment", () => {
expect("Hello World").toContain("World");
expect(["apple", "banana", "cherry"]).toContain("banana");
});

Array Matchers 📋

// toContain() / toContainEqual()
test("array matchers", () => {
const fruits = ["apple", "banana", "cherry"];
expect(fruits).toContain("banana");
expect(fruits).toHaveLength(3);
// Deep equality in arrays
const users = [
{ id: 1, name: "John" },
{ id: 2, name: "Jane" },
];
expect(users).toContainEqual({ id: 1, name: "John" });
});
// toHaveLength()
test("array length", () => {
expect([1, 2, 3]).toHaveLength(3);
expect("hello").toHaveLength(5);
});

Object Matchers 🎯

// toHaveProperty()
test("object properties", () => {
const user = { name: "John", age: 30, email: "john@example.com" };
expect(user).toHaveProperty("name");
expect(user).toHaveProperty("age", 30);
expect(user).toHaveProperty("address", undefined);
// Nested properties
const nested = { user: { profile: { name: "John" } } };
expect(nested).toHaveProperty("user.profile.name", "John");
});
// toMatchObject() - partial match
test("partial object match", () => {
const user = { name: "John", age: 30, email: "john@example.com" };
expect(user).toMatchObject({ name: "John", age: 30 });
expect(user).toMatchObject({ name: "John" });
});

Exception Matchers ⚠️

// toThrow() - check if function throws
test("exception matchers", () => {
// Throw any error
expect(() => {
throw new Error("Something went wrong");
}).toThrow();
// Throw specific error message
expect(() => {
throw new Error("Something went wrong");
}).toThrow("Something went wrong");
// Throw error matching regex
expect(() => {
throw new Error("Invalid input");
}).toThrow(/Invalid/);
// Throw specific error type
expect(() => {
throw new TypeError("Type error");
}).toThrow(TypeError);
// Not throw
expect(() => {
return 42;
}).not.toThrow();
});
// Function that throws
function divide(a, b) {
if (b === 0) {
throw new Error("Division by zero");
}
return a / b;
}
test("division by zero", () => {
expect(() => divide(10, 0)).toThrow("Division by zero");
expect(() => divide(10, 2)).not.toThrow();
expect(divide(10, 2)).toBe(5);
});

Mock Matchers 🎭

// toHaveBeenCalled() / toHaveBeenCalledTimes()
test("mock call matchers", () => {
const mockFn = jest.fn();
mockFn();
mockFn("arg1", "arg2");
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledTimes(2);
expect(mockFn).toHaveBeenCalledWith("arg1", "arg2");
expect(mockFn).toHaveBeenLastCalledWith("arg1", "arg2");
expect(mockFn).toHaveBeenNthCalledWith(1); // First call
expect(mockFn).toHaveBeenNthCalledWith(2, "arg1", "arg2"); // Second call
});
// toHaveReturned() / toHaveReturnedWith()
test("mock return matchers", () => {
const mockFn = jest
.fn()
.mockReturnValueOnce(10)
.mockReturnValueOnce(20)
.mockReturnValue(30);
expect(mockFn()).toBe(10);
expect(mockFn()).toBe(20);
expect(mockFn()).toBe(30);
expect(mockFn).toHaveReturned();
expect(mockFn).toHaveReturnedWith(10);
expect(mockFn).toHaveReturnedTimes(3);
});

Custom Matchers 🎨

// Extend Jest matchers
expect.extend({
toBeWithinRange(received, floor, ceiling) {
const pass = received >= floor && received <= ceiling;
if (pass) {
return {
message: () =>
`expected ${received} not to be within range ${floor} - ${ceiling}`,
pass: true,
};
} else {
return {
message: () =>
`expected ${received} to be within range ${floor} - ${ceiling}`,
pass: false,
};
}
},
});
test("custom matcher", () => {
expect(5).toBeWithinRange(1, 10);
expect(15).not.toBeWithinRange(1, 10);
});

Async Testing

Promises 🔄

// Return promise
test("promise resolves", () => {
return fetchData().then((data) => {
expect(data).toBe("data");
});
});
// Using async/await
test("async/await", async () => {
const data = await fetchData();
expect(data).toBe("data");
});
// Promise rejection
test("promise rejects", async () => {
await expect(fetchDataError()).rejects.toThrow("Error message");
// Or with try/catch
try {
await fetchDataError();
fail("should have thrown");
} catch (error) {
expect(error.message).toBe("Error message");
}
});
// Promise resolution
test("promise resolves with value", async () => {
await expect(Promise.resolve("success")).resolves.toBe("success");
});

Async Functions Examples 🌍

// Fetch API
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error("Failed to fetch user");
}
return response.json();
}
test("fetches user data", async () => {
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve({ id: 1, name: "John" }),
}),
);
const user = await fetchUser(1);
expect(user).toEqual({ id: 1, name: "John" });
expect(fetch).toHaveBeenCalledWith("/api/users/1");
});
// Multiple async operations
test("multiple async operations", async () => {
const [user, posts] = await Promise.all([fetchUser(1), fetchPosts(1)]);
expect(user).toBeDefined();
expect(posts).toHaveLength(5);
});

Timers ⏱️

// Fake timers
jest.useFakeTimers();
test("timer test", () => {
const callback = jest.fn();
setTimeout(callback, 1000);
expect(callback).not.toHaveBeenCalled();
// Fast-forward time
jest.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalledTimes(1);
// Run all pending timers
jest.runAllTimers();
// Run only pending timers
jest.runOnlyPendingTimers();
// Clear all timers
jest.clearAllTimers();
});
// Restore real timers
afterEach(() => {
jest.useRealTimers();
});
// setInterval example
test("interval test", () => {
const callback = jest.fn();
setInterval(callback, 100);
jest.advanceTimersByTime(500);
expect(callback).toHaveBeenCalledTimes(5);
});

Mocking

Function Mocks 🎭

// jest.fn() - create mock function
test("mock function", () => {
const mockFn = jest.fn();
mockFn("arg1", "arg2");
expect(mockFn).toHaveBeenCalledWith("arg1", "arg2");
expect(mockFn).toHaveBeenCalledTimes(1);
});
// Mock return values
test("mock return value", () => {
const mockFn = jest.fn();
mockFn.mockReturnValue(42);
expect(mockFn()).toBe(42);
// Multiple return values
mockFn.mockReturnValueOnce(10).mockReturnValueOnce(20).mockReturnValue(30);
expect(mockFn()).toBe(10);
expect(mockFn()).toBe(20);
expect(mockFn()).toBe(30);
expect(mockFn()).toBe(30);
});
// Mock implementation
test("mock implementation", () => {
const mockFn = jest.fn((a, b) => a + b);
expect(mockFn(2, 3)).toBe(5);
// Change implementation
mockFn.mockImplementation((a, b) => a * b);
expect(mockFn(2, 3)).toBe(6);
});
// Mock async functions
test("mock async function", async () => {
const mockAsyncFn = jest.fn().mockResolvedValue("success");
const result = await mockAsyncFn();
expect(result).toBe("success");
// Mock rejection
const mockRejectFn = jest.fn().mockRejectedValue(new Error("failed"));
await expect(mockRejectFn()).rejects.toThrow("failed");
});

Module Mocks 📦

// jest.mock() - mock entire module
jest.mock("./api");
import { fetchData } from "./api";
test("mocked module", async () => {
fetchData.mockResolvedValue({ data: "test" });
const result = await fetchData();
expect(result).toEqual({ data: "test" });
});
// Manual mock implementation
jest.mock("./utils", () => ({
...jest.requireActual("./utils"),
helperFunction: jest.fn(() => "mocked"),
}));
// Mock with factory
jest.mock("./api", () => {
return {
fetchData: jest.fn(),
postData: jest.fn(),
};
});
// Partial mock (keep some real implementations)
jest.mock("./utils", () => ({
...jest.requireActual("./utils"),
expensiveFunction: jest.fn(),
}));

Spy Functions 👁️

// jest.spyOn() - spy on object method
test("spy on method", () => {
const obj = {
method: () => "original",
};
const spy = jest.spyOn(obj, "method");
obj.method();
expect(spy).toHaveBeenCalled();
// Mock implementation
spy.mockReturnValue("mocked");
expect(obj.method()).toBe("mocked");
// Restore original
spy.mockRestore();
expect(obj.method()).toBe("original");
});
// Spy on class method
class Calculator {
add(a, b) {
return a + b;
}
}
test("spy on class method", () => {
const calc = new Calculator();
const spy = jest.spyOn(calc, "add");
calc.add(2, 3);
expect(spy).toHaveBeenCalledWith(2, 3);
spy.mockReturnValue(10);
expect(calc.add(2, 3)).toBe(10);
});

Mock Files 📁

// __mocks__/api.js - manual mock file
// Place in __mocks__ directory next to module
export const fetchData = jest.fn(() => Promise.resolve({ data: "mocked" }));
export const postData = jest.fn(() => Promise.resolve({ success: true }));
// Use manual mock
jest.mock("./api");
// Clear mocks between tests
beforeEach(() => {
jest.clearAllMocks();
});

Common Mocking Patterns 🎯

// Mock fetch
global.fetch = jest.fn();
test("mock fetch", async () => {
fetch.mockResolvedValueOnce({
ok: true,
json: async () => ({ data: "test" }),
});
const response = await fetch("/api/data");
const data = await response.json();
expect(data).toEqual({ data: "test" });
});
// Mock localStorage
const localStorageMock = {
getItem: jest.fn(),
setItem: jest.fn(),
removeItem: jest.fn(),
clear: jest.fn(),
};
global.localStorage = localStorageMock;
// Mock Date
const mockDate = new Date("2024-01-01");
jest.spyOn(global, "Date").mockImplementation(() => mockDate);
// Mock Math.random
jest.spyOn(Math, "random").mockReturnValue(0.5);

Setup & Teardown

Global Setup 🔧

// beforeEach() - runs before each test
beforeEach(() => {
// Setup code
initializeDatabase();
});
// afterEach() - runs after each test
afterEach(() => {
// Cleanup code
cleanupDatabase();
jest.clearAllMocks();
});
// beforeAll() - runs once before all tests
beforeAll(async () => {
await setupTestDatabase();
});
// afterAll() - runs once after all tests
afterAll(async () => {
await teardownTestDatabase();
});

Scoped Setup 📦

describe("User service", () => {
let userService;
beforeEach(() => {
userService = new UserService();
});
afterEach(() => {
userService.cleanup();
});
test("creates user", () => {
const user = userService.create({ name: "John" });
expect(user).toBeDefined();
});
test("deletes user", () => {
const user = userService.create({ name: "John" });
userService.delete(user.id);
expect(userService.findById(user.id)).toBeUndefined();
});
});

Setup Files 📝

// setupTests.ts or setupTests.js
import "@testing-library/jest-dom";
// Global test utilities
global.testUtils = {
createMockUser: () => ({ id: 1, name: "Test User" }),
};
// Mock console methods to reduce noise
global.console = {
...console,
log: jest.fn(),
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
};
// jest.config.js
module.exports = {
setupFilesAfterEnv: ["<rootDir>/src/setupTests.ts"],
};

Configuration

Jest Config Options ⚙️

jest.config.js
module.exports = {
// Test environment
testEnvironment: "node", // 'node' | 'jsdom'
// Coverage settings
collectCoverage: true,
coverageDirectory: "coverage",
coverageReporters: ["text", "lcov", "html"],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
// Test patterns
testMatch: ["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)"],
// Ignore patterns
testPathIgnorePatterns: ["/node_modules/", "/dist/", "/build/"],
// Module resolution
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/src/$1",
"\\.(css|less|scss|sass)$": "identity-obj-proxy",
},
// Transform
transform: {
"^.+\\.tsx?$": "ts-jest",
"^.+\\.jsx?$": "babel-jest",
},
// Setup files
setupFilesAfterEnv: ["<rootDir>/src/setupTests.ts"],
// Verbose output
verbose: true,
// Max workers (parallelization)
maxWorkers: "50%",
// Clear mocks
clearMocks: true,
resetMocks: true,
restoreMocks: true,
};

TypeScript Configuration 🔷

// jest.config.js with ts-jest
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src'],
testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
transform: {
'^.+\\.ts$': 'ts-jest',
},
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
};
// tsconfig.json for tests
{
"compilerOptions": {
"types": ["jest", "@types/jest"],
"esModuleInterop": true,
}
}

Environment Variables 🔐

.env.test
DATABASE_URL=postgresql://test:test@localhost:5432/testdb
API_KEY=test-key
// jest.config.js
module.exports = {
setupFiles: ['<rootDir>/jest.setup.js'],
};
// jest.setup.js
require('dotenv').config({ path: '.env.test' });

Best Practices

Test Organization ✅

// ✅ Good: Group related tests
describe("UserService", () => {
describe("createUser", () => {
test("creates user with valid data", () => {
// ...
});
test("throws error with invalid email", () => {
// ...
});
});
describe("deleteUser", () => {
test("deletes existing user", () => {
// ...
});
});
});
// ✅ Good: One assertion per test (when possible)
test("user has required fields", () => {
const user = createUser({ name: "John", email: "john@example.com" });
expect(user).toHaveProperty("id");
expect(user).toHaveProperty("name");
expect(user).toHaveProperty("email");
});
// ✅ Good: Descriptive test names
test("throws ValidationError when email is missing", () => {
// ...
});

Test Isolation ✅

// ✅ Good: Each test is independent
describe("Calculator", () => {
let calculator;
beforeEach(() => {
calculator = new Calculator(); // Fresh instance for each test
});
test("adds numbers", () => {
expect(calculator.add(2, 3)).toBe(5);
});
test("subtracts numbers", () => {
expect(calculator.subtract(5, 3)).toBe(2);
});
});
// ❌ Bad: Tests depend on each other
let sharedState = 0;
test("increments shared state", () => {
sharedState++;
expect(sharedState).toBe(1);
});
test("uses shared state", () => {
expect(sharedState).toBe(1); // Depends on previous test!
});

Mocking Best Practices ✅

// ✅ Good: Clear mock setup
describe("UserService", () => {
let mockApi;
beforeEach(() => {
mockApi = {
fetchUser: jest.fn(),
createUser: jest.fn(),
};
});
afterEach(() => {
jest.clearAllMocks();
});
test("fetches user", async () => {
mockApi.fetchUser.mockResolvedValue({ id: 1, name: "John" });
// ...
});
});
// ✅ Good: Mock only what you need
jest.mock("./api", () => ({
fetchData: jest.fn(),
// Don't mock everything, only what's needed
}));
// ❌ Bad: Over-mocking
jest.mock("./api"); // Mocks everything unnecessarily

Async Testing Best Practices ✅

// ✅ Good: Always await async operations
test("fetches data", async () => {
const data = await fetchData();
expect(data).toBeDefined();
});
// ✅ Good: Test both success and error cases
test("handles fetch error", async () => {
fetch.mockRejectedValueOnce(new Error("Network error"));
await expect(fetchData()).rejects.toThrow("Network error");
});
// ❌ Bad: Missing await
test("fetches data", () => {
const data = fetchData(); // Missing await!
expect(data).toBeDefined(); // This will fail
});

Common Pitfalls

Async/Await Issues ⚠️

Warning: Always use await with async operations or return promises from tests.

// ❌ Wrong: Missing await
test("async test", () => {
const result = fetchData(); // Returns Promise, not data
expect(result).toBe("data"); // Fails: comparing Promise to string
});
// ✅ Correct: Use await
test("async test", async () => {
const result = await fetchData();
expect(result).toBe("data");
});
// ✅ Alternative: Return promise
test("async test", () => {
return fetchData().then((result) => {
expect(result).toBe("data");
});
});

Mock Not Restored ⚠️

// ❌ Wrong: Mock affects other tests
test("test 1", () => {
jest.spyOn(Math, "random").mockReturnValue(0.5);
// Test code
// Mock not restored!
});
test("test 2", () => {
// Math.random still mocked from test 1!
expect(Math.random()).toBe(0.5); // Unexpected behavior
});
// ✅ Correct: Restore mocks
afterEach(() => {
jest.restoreAllMocks();
});
// Or manually
test("test 1", () => {
const spy = jest.spyOn(Math, "random").mockReturnValue(0.5);
// Test code
spy.mockRestore();
});

Floating Point Comparisons ⚠️

// ❌ Wrong: Direct equality with floats
test("floating point", () => {
expect(0.1 + 0.2).toBe(0.3); // Fails due to precision
});
// ✅ Correct: Use toBeCloseTo()
test("floating point", () => {
expect(0.1 + 0.2).toBeCloseTo(0.3, 5);
});

Test Timeout Issues ⚠️

// Increase timeout for slow tests
test("slow operation", async () => {
const result = await slowOperation();
expect(result).toBeDefined();
}, 10000); // 10 second timeout
// Or set globally
jest.setTimeout(10000);

Object Reference Equality ⚠️

// ❌ Wrong: Using toBe() for object comparison
test("object equality", () => {
expect({ a: 1 }).toBe({ a: 1 }); // Fails: different references
});
// ✅ Correct: Use toEqual() for deep equality
test("object equality", () => {
expect({ a: 1 }).toEqual({ a: 1 }); // Passes: deep comparison
});

Mock Implementation Not Called ⚠️

// ❌ Wrong: Mock not set up before use
test("uses mock", () => {
const result = fetchData(); // Called before mock setup
jest.mock("./api", () => ({
fetchData: jest.fn(() => "mocked"),
}));
expect(result).toBe("mocked"); // Fails
});
// ✅ Correct: Set up mocks before use
jest.mock("./api", () => ({
fetchData: jest.fn(() => "mocked"),
}));
test("uses mock", () => {
const result = fetchData();
expect(result).toBe("mocked"); // Passes
});