Jest
Jest cheat sheet with matchers, mock functions, async testing, snapshot tests, and TypeScript testing patterns with code examples.
Setup & Configuration
Installing and configuring Jest for your project
Setting up Jest in your JavaScript/TypeScript project
# Install Jest
npm install --save-dev jest
# For TypeScript support
npm install --save-dev @types/jest ts-jest
# package.json scripts
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
}
}Basic Testing
Writing your first tests with Jest
Organizing tests with describe, it, and test blocks
// Basic test structure
describe('Calculator', () => {
test('adds 1 + 2 to equal 3', () => {
expect(1 + 2).toBe(3)
})
it('subtracts 5 - 2 to equal 3', () => {
expect(5 - 2).toBe(3)
})
})
// Setup and teardown
describe('User Service', () => {
let service
beforeEach(() => {
service = new UserService()
})
afterEach(() => {
service.cleanup()
})
beforeAll(async () => {
await database.connect()
})
afterAll(async () => {
await database.disconnect()
})
})Matchers
Jest matchers for different types of assertions
Most frequently used Jest matchers for assertions
// Equality
expect(2 + 2).toBe(4) // Exact equality ===
expect({name: 'jest'}).toEqual({name: 'jest'}) // Deep equality
// Truthiness
expect(value).toBeTruthy()
expect(value).toBeFalsy()
expect(value).toBeNull()
expect(value).toBeUndefined()
expect(value).toBeDefined()
// Numbers
expect(2 + 2).toBeGreaterThan(3)
expect(2 + 2).toBeGreaterThanOrEqual(4)
expect(2 + 2).toBeLessThan(5)
expect(0.1 + 0.2).toBeCloseTo(0.3) // For floating point
// Strings
expect('team').toMatch(/I/)
expect('Christoph').toMatch('stop')
// Arrays and Iterables
expect(['Alice', 'Bob']).toContain('Alice')
expect(new Set(['Alice', 'Bob'])).toContain('Alice')
expect(['Alice', 'Bob']).toHaveLength(2)Creating your own Jest matchers for domain-specific assertions
// Define custom matcher
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,
}
}
},
})
// Use custom matcher
test('numeric ranges', () => {
expect(100).toBeWithinRange(90, 110)
expect(101).not.toBeWithinRange(0, 100)
})Mocking
Mocking functions, modules, and dependencies
Creating and using mock functions (spies)
// Create a mock function
const mockFn = jest.fn()
mockFn('arg1', 'arg2')
// Mock with return value
const mockCallback = jest.fn(x => x * 2)
expect(mockCallback(5)).toBe(10)
// Mock implementation
const mockImplementation = jest.fn()
.mockImplementation((x) => x + 1)
.mockImplementationOnce((x) => x + 2) // First call only
// Assertions on mocks
expect(mockFn).toHaveBeenCalled()
expect(mockFn).toHaveBeenCalledTimes(1)
expect(mockFn).toHaveBeenCalledWith('arg1', 'arg2')
expect(mockFn).toHaveBeenLastCalledWith('arg1', 'arg2')Mocking entire modules and their exports
// Mock entire module
jest.mock('./api')
import * as api from './api'
// Mock with factory
jest.mock('./config', () => ({
apiUrl: 'http://localhost',
timeout: 1000
}))
// Partial mocks
jest.mock('./math', () => ({
...jest.requireActual('./math'),
add: jest.fn(() => 5)
}))
// Mock ES6 classes
jest.mock('./UserService')
import UserService from './UserService'
const mockUserService = UserService as jest.MockedClass<typeof UserService>Async Testing
Testing asynchronous code with promises and callbacks
Different patterns for testing asynchronous code
// Using async/await
test('async data fetch', async () => {
const data = await fetchData()
expect(data).toBe('data')
})
// Using promises
test('promise resolution', () => {
return fetchData().then(data => {
expect(data).toBe('data')
})
})
// Expect promises
test('resolves/rejects', async () => {
await expect(fetchData()).resolves.toBe('data')
await expect(fetchError()).rejects.toThrow('error')
})
// Testing callbacks
test('callback test', (done) => {
function callback(data) {
try {
expect(data).toBe('data')
done()
} catch (error) {
done(error)
}
}
fetchDataCallback(callback)
})Timers & Dates
Testing code that uses setTimeout, setInterval, and Date
Controlling time in tests with Jest fake timers
// Enable fake timers
beforeEach(() => {
jest.useFakeTimers()
})
afterEach(() => {
jest.useRealTimers()
})
test('timer test', () => {
const callback = jest.fn()
setTimeout(callback, 1000)
// Fast-forward time
jest.advanceTimersByTime(1000)
expect(callback).toHaveBeenCalledTimes(1)
})
// Run all timers
jest.runAllTimers()
// Run only pending timers
jest.runOnlyPendingTimers()Testing React Components
Testing React components with Jest and React Testing Library
Testing React components with React Testing Library
// Basic component test
import { render, screen, fireEvent } from '@testing-library/react'
import Button from './Button'
test('renders button with text', () => {
render(<Button>Click me</Button>)
const button = screen.getByText(/click me/i)
expect(button).toBeInTheDocument()
})
// Testing events
test('calls onClick handler', () => {
const handleClick = jest.fn()
render(<Button onClick={handleClick}>Click</Button>)
fireEvent.click(screen.getByText('Click'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
// Testing async components
test('loads user data', async () => {
render(<UserProfile id={1} />)
expect(screen.getByText(/loading/i)).toBeInTheDocument()
const userName = await screen.findByText('John Doe')
expect(userName).toBeInTheDocument()
})Code Coverage
Measuring and improving test coverage
Setting up and interpreting code coverage reports
// jest.config.js
module.exports = {
collectCoverage: true,
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/index.js',
'!src/**/*.test.{js,jsx,ts,tsx}',
'!src/**/*.stories.{js,jsx,ts,tsx}'
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
},
coverageReporters: ['text', 'lcov', 'html']
}
// Run with coverage
npm test -- --coverage
npm test -- --coverage --watchAll=falseBest Practices
Testing best practices and common patterns
Guidelines for writing maintainable and effective tests
// Good: Descriptive test names
test('should return user name when valid ID is provided', () => {})
// Good: Arrange-Act-Assert pattern
test('calculates total price with tax', () => {
// Arrange
const items = [{ price: 10 }, { price: 20 }]
const taxRate = 0.1
// Act
const total = calculateTotal(items, taxRate)
// Assert
expect(total).toBe(33)
})
// Good: Test one thing at a time
test('validates email format', () => {
expect(isValidEmail('test@example.com')).toBe(true)
})
test('rejects invalid email format', () => {
expect(isValidEmail('invalid')).toBe(false)
})
// Good: Use beforeEach for setup
let service
beforeEach(() => {
service = new UserService()
jest.clearAllMocks()
})