Jest
Jest cheat sheet with matchers, mock functions, async testing, snapshot tests, and TypeScript testing patterns with code examples.
Sign in to mark items as known and track your progress.
Sign inSetup & Configuration
Installing and configuring Jest for your project
Installation & Basic Setup
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
Test Structure & Basics
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
Common Matchers
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)Custom Matchers
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
Function Mocks
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')Module Mocks
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
Testing Promises & Async/Await
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
Fake Timers
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
Component Testing Basics
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
Coverage Configuration
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
Testing Best Practices
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()
})