Jest can look intimidating until you realize the core ideas are delightfully small. In this tutorial we’ll start with the most confusingly simple bit — it()
— and build up to a practical toolbox you’ll use every day: grouping with describe
, resetting with beforeEach
, using matchers, handling async code, and a tiny taste of mocks. Examples work in Node, React, and React Native (with jest-expo
or react-native
preset).
🔤 What is it()
?
Short answer: it()
is the same as test()
. Both define a single test case.
// identical in Jest
it('adds numbers', () => {
expect(1 + 2).toBe(3);
});
test('adds numbers', () => {
expect(1 + 2).toBe(3);
});
Why two names? Style. Many people prefer it()
inside a describe()
because it reads like a sentence:
describe('sum()', () => {
it('adds two numbers', () => { /* ... */ });
it('handles negatives', () => { /* ... */ });
});
Tip: Choose one style and stick to it for consistency. I’ll use
it()
here.
🧱 The Minimal Test Structure
// sum.js
export const sum = (a, b) => a + b;
// sum.test.js
import { sum } from './sum';
describe('sum()', () => {
it('adds two numbers', () => {
expect(sum(1, 2)).toBe(3);
});
});
Run with:
npm test
If you see a green PASS
, you’re testing! 🎉
🧰 Grouping & Setup: describe
, beforeEach
, afterEach
When tests grow, you’ll want a clean place to reset variables/mocks.
describe('shopping cart', () => {
let cart;
beforeEach(() => {
cart = [];
});
afterEach(() => {
// clean up if needed
});
it('starts empty', () => {
expect(cart).toHaveLength(0);
});
it('adds items', () => {
cart.push('apple');
expect(cart).toContain('apple');
});
});
-
describe()
groups related tests. -
beforeEach()
runs before everyit()
in the group. -
afterEach()
runs after each test (great for resetting timers, mocks, DOM, etc.).
🎯 Matchers You’ll Use Daily
expect(2 + 2).toBe(4); // strict equality
expect({a: 1}).toEqual({a: 1}); // deep equality (objects/arrays)
expect('Hello Jest').toMatch(/Jest/); // regex/string match
expect([1, 2, 3]).toContain(2); // arrays
expect([1, 2, 3]).toHaveLength(3); // length
expect(value).toBeTruthy(); // truthiness
expect(() => JSON.parse('nope')).toThrow(); // errors
Rule of thumb:
toBe
for primitives,toEqual
for objects/arrays.
⚡ Async Tests: Promises & async/await
Jest makes async easy. Pick one pattern and stick to it.
Option A — return a Promise:
it('resolves value', () => {
return Promise.resolve(42).then(v => {
expect(v).toBe(42);
});
});
Option B — async/await (cleanest):
it('resolves value (async/await)', async () => {
const v = await Promise.resolve(42);
expect(v).toBe(42);
});
Option C — .resolves / .rejects sugar:
it('resolves with sugar', async () => {
await expect(Promise.resolve(3)).resolves.toBe(3);
});
it('rejects with error', async () => {
await expect(Promise.reject(new Error('boom'))).rejects.toThrow('boom');
});
🧪 Testing UI (React / React Native)
For components, add Testing Library to get human-friendly queries:
# React (web)
npm i -D @testing-library/react @testing-library/jest-dom
# React Native / Expo
npm i -D @testing-library/react-native @testing-library/jest-native
jest.setup.ts (runs before tests):
import '@testing-library/jest-native/extend-expect';
// for web React: import '@testing-library/jest-dom';
React Native snippet:
// Hello.tsx
import React from 'react';
import { Text } from 'react-native';
export const Hello = ({ name }: { name: string }) => <Text>Hello {name}</Text>;
// Hello.test.tsx
import React from 'react';
import { render } from '@testing-library/react-native';
import { Hello } from './Hello';
it('renders greeting', () => {
const { getByText } = render(<Hello name="Cathy" />);
expect(getByText('Hello Cathy')).toBeTruthy();
});
RN note: Use
jest-expo
orreact-native
preset injest.config.js
, and set uptransformIgnorePatterns
for RN packages.
🧪 Mocks in 60 Seconds: jest.fn()
and jest.mock()
1) jest.fn()
— fake a callback
function greet(name, logger) {
logger(`Hello ${name}`);
}
it('logs greeting', () => {
const logger = jest.fn();
greet('Cathy', logger);
expect(logger).toHaveBeenCalledWith('Hello Cathy');
});
2) jest.mock()
— fake a module
// file: api.ts
export async function fetchUser(id) {
const res = await fetch(`/users/${id}`);
return res.json();
}
// file: profile.ts
import { fetchUser } from './api';
export async function getProfileName(id) {
const user = await fetchUser(id);
return user.name;
}
// file: profile.test.ts
import { getProfileName } from './profile';
import * as api from './api';
jest.mock('./api'); // turns all exports into mock functions
it('returns mocked name', async () => {
(api.fetchUser as jest.Mock).mockResolvedValue({ name: 'Cathy' });
await expect(getProfileName(1)).resolves.toBe('Cathy');
});
Start with
jest.fn()
for callbacks; reach forjest.mock()
only when a real module is inconvenient in tests.
🧪 Snapshots (Optional, Handy for UI)
it('matches snapshot', () => {
const tree = { title: 'My Card', items: [1, 2, 3] };
expect(tree).toMatchSnapshot();
});
Jest saves a snapshot file; future runs compare output to catch accidental changes. Great for stable UI or JSON. Avoid snapshotting huge or volatile objects.
⚙️ Minimal Configs (copy–paste)
Node/JS
// jest.config.js
export default {
testEnvironment: 'node',
setupFilesAfterEnv: [],
};
React (web)
// jest.config.js
export default {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
};
React Native / Expo
// jest.config.js
export default {
preset: 'jest-expo', // or 'react-native'
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
transformIgnorePatterns: [
'node_modules/(?!(@react-native|react-native|@react-native-community|react-clone-referenced-element)/)',
],
};
jest.setup.ts
// web:
/*
import '@testing-library/jest-dom';
*/
// react-native:
/*
import '@testing-library/jest-native/extend-expect';
*/
🧭 Recommended Testing Style
-
One behavior per
it()
(small, readable tests). - Prefer
it('does X')
with present-tense, human language. - Use
beforeEach
sparingly — explicit local setup can be clearer. - Test behavior, not implementation (assert what the user sees/gets).
- Keep mocks minimal; real code is more trustworthy when practical.
✅ TL;DR
-
it()
=test()
; both define one test. - Group with
describe()
, reset withbeforeEach()
. - Learn 8–10 matchers; you’ll use them daily.
- Async is easy with
async/await
and.resolves/.rejects
. - Start with
jest.fn()
; reach forjest.mock()
later. - For UI: use Testing Library + a small
jest.setup.ts
.
If you can write the examples above, you already know 80% of Jest you’ll need in real projects. Happy testing! 🚀
Top comments (0)