# Testing with Jest
Types describe valid inputs and outputs; tests check behavior. A function can return a number and still calculate the wrong number. Jest runs tests, reports failed assertions and provides tools for isolating dependencies.
| Test level | What it checks |
|---|---|
| Unit | A small function or module in isolation |
| Integration | Components working together, such as a route and data layer |
| End-to-end | A user's flow through the running application |
Start with fast, deterministic unit tests. The following examples use Jest 29/30, plain JavaScript, CommonJS and the Node test environment in an example application. Use "type": "commonjs" in its package manifest; no TypeScript transformer or browser environment is needed for these tests.
# Arrange, act, assert
Create calculator.js. Its contract accepts numeric operands and rejects division by zero:
function sum(a, b) {
return a + b;
}
function divide(a, b) {
if (b === 0) throw new RangeError('Division by zero');
return a / b;
}
module.exports = { sum, divide };
Create calculator.test.js:
const { sum, divide } = require('./calculator');
describe('calculator', () => {
test.each([
[2, 5, 7],
[0, 0, 0],
[-2, 5, 3]
])('sum(%i, %i) is %i', (a, b, expected) => {
const result = sum(a, b);
expect(result).toBe(expected);
});
test('divides numbers', () => {
expect(divide(6, 2)).toBe(3);
});
test('rejects a zero divisor', () => {
expect(() => divide(6, 0)).toThrow('Division by zero');
});
});
Arrange supplies inputs, act calls the operation, and assert checks the result. describe groups behavior, test names a case, and expect makes an assertion. Boundary cases and a specified failure are more useful than asserting that sum(2, 5) is not some unrelated number.
| Matcher | Use |
|---|---|
toBe | Primitive equality or object identity (Object.is) |
toEqual | Structural comparison of arrays/objects |
toThrow | A supplied function throws; wrap the call rather than invoking it immediately |
resolves / rejects | A Promise fulfills/rejects; await or return the assertion |
For example, expect({ total: sum(2, 5) }).toEqual({ total: 7 }) compares content; toBe would compare two different object references.
# Async behavior and a small mock
In greeting.js, inject the data-loading function instead of making the unit test depend on a real network:
async function greetingFor(id, loadStudent) {
const student = await loadStudent(id);
return `Hello, ${student.name}`;
}
module.exports = { greetingFor };
In greeting.test.js:
const { greetingFor } = require('./greeting');
test('greets the loaded student', async () => {
const loadStudent = jest.fn().mockResolvedValue({ name: 'Ada' });
await expect(greetingFor(1, loadStudent)).resolves.toBe('Hello, Ada');
expect(loadStudent).toHaveBeenCalledWith(1);
});
test('propagates a loading failure', async () => {
const loadStudent = jest.fn().mockRejectedValue(new Error('Offline'));
await expect(greetingFor(1, loadStudent)).rejects.toThrow('Offline');
});
jest.fn() records calls and can provide controlled results. Mock the external loader, not greetingFor itself: replacing the behavior under test would prove nothing. Each test creates its own mock, preventing call history from leaking between cases.
Without await or return, a test can finish before its Promise assertion runs. Avoid real network calls and arbitrary sleep delays in unit tests; control external results instead.
# Running tests
With Jest installed as a development dependency, add these scripts:
{
"scripts": {
"test": "jest",
"test:watch": "jest --watchAll",
"test:coverage": "jest --coverage"
}
}
Run all tests with npm test, or focus on the calculator with npm test -- calculator.test.js. Watch mode repeats tests during editing. Coverage tells you which code executed, not whether the assertions detect all mistakes.
Jest's default Node environment has no DOM. Browser-element tests need a separately configured environment such as jest-environment-jsdom. TypeScript/ESM tests also need compatible Jest configuration; a Webpack ts-loader rule does not configure Jest. Run type checking separately if the chosen test transformer only transpiles.
Try it: introduce a mistake into sum and verify a test fails, then restore the implementation. Add a case for two negative numbers and explain what the existing tests did not cover.
Jest getting started (opens new window) · Async tests (opens new window) · Matchers (opens new window)