Jest is a popular JavaScript testing framework that makes writing and running tests easy and enjoyable. See Javascript Guide for more context. See Javascript Guide for more context.
Installation and Setup
Install Jest
npm install --save-dev jest
Configure package.json
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
}
}
Create Test File
// math.test.js
describe("Math operations", () => {
test("adds two numbers", () => {
expect(2 + 2).toBe(4);
});
});
Run Tests
npm test
Basic Test Structure
Test Syntax
test("description", () => {
// Test code
});
// Or using it()
it("description", () => {
// Test code
});
Describe Blocks
describe("Calculator", () => {
test("adds numbers", () => {
expect(add(2, 3)).toBe(5);
});
test("subtracts numbers", () => {
expect(subtract(5, 3)).toBe(2);
});
});
Matchers
Equality
test("equality matchers", () => {
expect(4).toBe(4); // Strict equality
expect({ a: 1 }).toEqual({ a: 1 }); // Deep equality
expect(null).toBeNull();
expect(undefined).toBeUndefined();
expect(true).toBeDefined();
});
Truthiness
test("truthiness", () => {
expect(true).toBeTruthy();
expect(false).toBeFalsy();
expect(1).toBeTruthy();
expect(0).toBeFalsy();
});
Numbers
test("number matchers", () => {
expect(4).toBeGreaterThan(3);
expect(3).toBeGreaterThanOrEqual(3);
expect(2).toBeLessThan(3);
expect(3).toBeLessThanOrEqual(3);
expect(0.1 + 0.2).toBeCloseTo(0.3);
});
Strings
test("string matchers", () => {
expect("hello").toMatch(/ell/);
expect("hello").toMatch("ell");
expect("hello").toContain("ell");
});
Arrays and Objects
test("array and object matchers", () => {
expect([1, 2, 3]).toContain(2);
expect([1, 2, 3]).toHaveLength(3);
expect({ a: 1 }).toHaveProperty("a");
expect({ a: 1 }).toHaveProperty("a", 1);
});
Exceptions
test("exception matchers", () => {
expect(() => {
throw new Error("Test error");
}).toThrow();
expect(() => {
throw new Error("Test error");
}).toThrow("Test error");
expect(() => {
throw new Error("Test error");
}).toThrow(Error);
});
Setup and Teardown
beforeEach and afterEach
describe("Database", () => {
let db;
beforeEach(() => {
db = new Database();
db.connect();
});
afterEach(() => {
db.disconnect();
});
test("saves data", () => {
db.save("key", "value");
expect(db.get("key")).toBe("value");
});
});
beforeAll and afterAll
describe("API", () => {
let server;
beforeAll(() => {
server = startServer();
});
afterAll(() => {
server.stop();
});
test("fetches data", async () => {
const data = await fetch("/api/data");
expect(data).toBeDefined();
});
});
Practical Examples
Testing Functions
// math.js
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// math.test.js
import { add, multiply } from "./math";
describe("Math functions", () => {
test("add returns sum", () => {
expect(add(2, 3)).toBe(5);
expect(add(-1, 1)).toBe(0);
expect(add(0, 0)).toBe(0);
});
test("multiply returns product", () => {
expect(multiply(2, 3)).toBe(6);
expect(multiply(-2, 3)).toBe(-6);
expect(multiply(0, 5)).toBe(0);
});
});
Testing Classes
// User.js
export class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
getInfo() {
return `${this.name} (${this.email})`;
}
isValidEmail() {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email);
}
}
// User.test.js
import { User } from "./User";
describe("User class", () => {
let user;
beforeEach(() => {
user = new User("Alice", "[email protected]");
});
test("creates user with name and email", () => {
expect(user.name).toBe("Alice");
expect(user.email).toBe("[email protected]");
});
test("getInfo returns formatted string", () => {
expect(user.getInfo()).toBe("Alice ([email protected])");
});
test("isValidEmail validates email", () => {
expect(user.isValidEmail()).toBe(true);
user.email = "invalid";
expect(user.isValidEmail()).toBe(false);
});
});
Testing Async Functions
// api.js
export async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
// api.test.js
import { fetchUser } from "./api";
describe("API functions", () => {
test("fetchUser returns user data", async () => {
const user = await fetchUser(1);
expect(user).toHaveProperty("id");
expect(user).toHaveProperty("name");
});
});
Testing with Mocks
// database.js
export class Database {
async save(key, value) {
// Actual database operation
}
}
// service.js
export class UserService {
constructor(db) {
this.db = db;
}
async createUser(name, email) {
const user = { name, email };
await this.db.save("user", user);
return user;
}
}
// service.test.js
import { UserService } from "./service";
describe("UserService", () => {
test("createUser saves to database", async () => {
const mockDb = {
save: jest.fn()
};
const service = new UserService(mockDb);
const user = await service.createUser("Alice", "[email protected]");
expect(mockDb.save).toHaveBeenCalledWith("user", user);
expect(mockDb.save).toHaveBeenCalledTimes(1);
});
});
Testing with Spies
// calculator.js
export class Calculator {
add(a, b) {
return a + b;
}
calculate(a, b, operation) {
return operation(a, b);
}
}
// calculator.test.js
import { Calculator } from "./calculator";
describe("Calculator", () => {
test("calculate calls operation", () => {
const calc = new Calculator();
const spy = jest.spyOn(calc, "add");
calc.calculate(2, 3, calc.add);
expect(spy).toHaveBeenCalledWith(2, 3);
spy.mockRestore();
});
});
Test Coverage
Generate Coverage Report
npm run test:coverage
Coverage Thresholds
{
"jest": {
"collectCoverageFrom": [
"src/**/*.js",
"!src/index.js"
],
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
}
}
}
}
Best Practices
Write Descriptive Test Names
// Good
test("returns sum of two positive numbers", () => {
expect(add(2, 3)).toBe(5);
});
// Avoid
test("add works", () => {
expect(add(2, 3)).toBe(5);
});
Test One Thing Per Test
// Good - focused test
test("add returns correct sum", () => {
expect(add(2, 3)).toBe(5);
});
// Avoid - testing multiple things
test("add and multiply work", () => {
expect(add(2, 3)).toBe(5);
expect(multiply(2, 3)).toBe(6);
});
Use Arrange-Act-Assert Pattern
test("user can login", () => {
// Arrange
const user = new User("alice", "password123");
// Act
const result = user.login("alice", "password123");
// Assert
expect(result).toBe(true);
});
Mock External Dependencies
// Good - mock external API
jest.mock("./api");
test("service fetches data", async () => {
const mockData = { id: 1, name: "Alice" };
api.fetchUser.mockResolvedValue(mockData);
const result = await service.getUser(1);
expect(result).toEqual(mockData);
});
Summary
- Jest: JavaScript testing framework
- test(): define a test
- expect(): make assertions
- Matchers: toBe(), toEqual(), toContain(), etc.
- Setup/Teardown: beforeEach(), afterEach()
- Mocks: jest.fn(), jest.mock()
- Spies: jest.spyOn()
- Coverage: measure code coverage
- Best practice: write focused, descriptive tests
Comments