Frameworks & Patterns
Jest Complete Guide

Jest Complete Guide: JavaScript Testing Framework for Modern Apps

Parul Dhingra - Senior Quality Analyst
Parul Dhingra13+ Years ExperienceHire Me

Senior Quality Analyst

Updated: 1/23/2026

Jest has become the standard testing framework for JavaScript and TypeScript projects, especially in the React ecosystem. Developed by Meta, it provides a complete testing solution out of the box - test runner, assertion library, mocking utilities, and code coverage - all with zero configuration for most projects.

This guide covers Jest from basic tests to advanced patterns that handle real-world testing challenges.

Why Jest?

Jest stands out for several reasons:

  • Zero configuration: Works out of the box for most projects
  • Fast and parallel: Runs tests in parallel with smart test ordering
  • Built-in mocking: Powerful mock functions and module mocking
  • Snapshot testing: Capture and compare output over time
  • Code coverage: Built-in coverage reporting
  • Watch mode: Re-runs tests on file changes
  • TypeScript support: First-class TypeScript integration

Jest is the default choice for React, Vue, and many Node.js projects.

Installation and Setup

Basic Installation

# npm
npm install --save-dev jest
 
# yarn
yarn add --dev jest
 
# With TypeScript
npm install --save-dev jest @types/jest ts-jest

package.json Configuration

{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage"
  }
}

TypeScript Setup

# Initialize ts-jest
npx ts-jest config:init

This creates jest.config.js:

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
}

Writing Tests

Basic Test Structure

// sum.js
function sum(a, b) {
  return a + b
}
module.exports = sum
 
// sum.test.js
const sum = require('./sum')
 
test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3)
})

Test Organization

describe('Calculator', () => {
  describe('addition', () => {
    test('adds positive numbers', () => {
      expect(sum(1, 2)).toBe(3)
    })
 
    test('adds negative numbers', () => {
      expect(sum(-1, -2)).toBe(-3)
    })
  })
 
  describe('subtraction', () => {
    test('subtracts numbers', () => {
      expect(subtract(5, 3)).toBe(2)
    })
  })
})

Test Aliases

Jest provides multiple ways to write tests:

// These are equivalent
test('description', () => {})
it('description', () => {})
 
// Focus on specific tests
test.only('runs only this test', () => {})
it.only('runs only this test', () => {})
 
// Skip tests
test.skip('skipped test', () => {})
it.skip('skipped test', () => {})
 
// Parameterized tests
test.each([
  [1, 2, 3],
  [2, 3, 5],
  [5, 5, 10],
])('adds %i + %i to equal %i', (a, b, expected) => {
  expect(sum(a, b)).toBe(expected)
})

Matchers

Matchers let you validate values in different ways.

Common Matchers

// Exact equality
expect(2 + 2).toBe(4)
 
// Object equality (deep)
expect({ name: 'John' }).toEqual({ name: 'John' })
 
// Truthiness
expect(null).toBeNull()
expect(undefined).toBeUndefined()
expect(value).toBeDefined()
expect(true).toBeTruthy()
expect(false).toBeFalsy()
 
// Numbers
expect(4).toBeGreaterThan(3)
expect(4).toBeGreaterThanOrEqual(4)
expect(4).toBeLessThan(5)
expect(0.1 + 0.2).toBeCloseTo(0.3)
 
// Strings
expect('hello world').toMatch(/world/)
expect('hello').toContain('ell')
 
// Arrays
expect([1, 2, 3]).toContain(2)
expect(['apple', 'banana']).toContainEqual('banana')
expect([1, 2, 3]).toHaveLength(3)
 
// Objects
expect({ name: 'John', age: 30 }).toHaveProperty('name')
expect({ name: 'John' }).toHaveProperty('name', 'John')
expect({ a: { b: 2 } }).toHaveProperty('a.b', 2)

Negation

expect(1 + 1).not.toBe(3)
expect([1, 2]).not.toContain(3)
expect({ a: 1 }).not.toHaveProperty('b')

Exception Matchers

function throwError() {
  throw new Error('Something went wrong')
}
 
test('throws an error', () => {
  expect(() => throwError()).toThrow()
  expect(() => throwError()).toThrow(Error)
  expect(() => throwError()).toThrow('Something went wrong')
  expect(() => throwError()).toThrow(/wrong/)
})

Use .toBe() for primitives and .toEqual() for objects and arrays. .toBe() uses Object.is() for comparison, while .toEqual() performs deep equality checking.

Setup and Teardown

Per-Test Setup

describe('Database tests', () => {
  let db
 
  beforeEach(() => {
    db = new Database()
    db.connect()
  })
 
  afterEach(() => {
    db.disconnect()
  })
 
  test('inserts data', () => {
    db.insert({ name: 'John' })
    expect(db.count()).toBe(1)
  })
 
  test('queries data', () => {
    db.insert({ name: 'Jane' })
    const result = db.query({ name: 'Jane' })
    expect(result).toHaveLength(1)
  })
})

One-Time Setup

describe('API tests', () => {
  let server
 
  beforeAll(async () => {
    server = await startServer()
  })
 
  afterAll(async () => {
    await server.close()
  })
 
  test('responds to GET /', async () => {
    const response = await fetch('http://localhost:3000/')
    expect(response.status).toBe(200)
  })
})

Scoped Setup

beforeAll(() => console.log('1 - beforeAll'))
afterAll(() => console.log('1 - afterAll'))
beforeEach(() => console.log('1 - beforeEach'))
afterEach(() => console.log('1 - afterEach'))
 
describe('Scoped tests', () => {
  beforeAll(() => console.log('2 - beforeAll'))
  afterAll(() => console.log('2 - afterAll'))
  beforeEach(() => console.log('2 - beforeEach'))
  afterEach(() => console.log('2 - afterEach'))
 
  test('example', () => console.log('2 - test'))
})
 
// Output:
// 1 - beforeAll
// 2 - beforeAll
// 1 - beforeEach
// 2 - beforeEach
// 2 - test
// 2 - afterEach
// 1 - afterEach
// 2 - afterAll
// 1 - afterAll

Mocking

Mock Functions

test('mock function basics', () => {
  const mockFn = jest.fn()
 
  mockFn('hello')
  mockFn('world')
 
  expect(mockFn).toHaveBeenCalled()
  expect(mockFn).toHaveBeenCalledTimes(2)
  expect(mockFn).toHaveBeenCalledWith('hello')
  expect(mockFn).toHaveBeenLastCalledWith('world')
})

Mock Return Values

const mockFn = jest.fn()
 
// Single return value
mockFn.mockReturnValue(42)
expect(mockFn()).toBe(42)
 
// Return once
mockFn.mockReturnValueOnce(1).mockReturnValueOnce(2).mockReturnValue(3)
 
expect(mockFn()).toBe(1)
expect(mockFn()).toBe(2)
expect(mockFn()).toBe(3)
expect(mockFn()).toBe(3)
 
// Custom implementation
mockFn.mockImplementation((x) => x * 2)
expect(mockFn(5)).toBe(10)

Mocking Modules

// api.js
export const fetchUser = (id) => fetch(`/users/${id}`)
 
// user.test.js
import { fetchUser } from './api'
import { getUser } from './user'
 
jest.mock('./api')
 
test('gets user', async () => {
  fetchUser.mockResolvedValue({
    json: () => Promise.resolve({ id: 1, name: 'John' }),
  })
 
  const user = await getUser(1)
  expect(user.name).toBe('John')
  expect(fetchUser).toHaveBeenCalledWith(1)
})

Partial Mocking

// Mock specific functions, keep others
jest.mock('./utils', () => ({
  ...jest.requireActual('./utils'),
  formatDate: jest.fn(() => '2024-01-01'),
}))

Spying

const video = {
  play() {
    return true
  },
  pause() {
    return true
  },
}
 
test('plays video', () => {
  const spy = jest.spyOn(video, 'play')
 
  video.play()
 
  expect(spy).toHaveBeenCalled()
  spy.mockRestore() // Restore original implementation
})

Async Testing

Promises

// Return the promise
test('resolves to data', () => {
  return fetchData().then((data) => {
    expect(data).toBe('data')
  })
})
 
// Using resolves/rejects
test('resolves to data', () => {
  return expect(fetchData()).resolves.toBe('data')
})
 
test('rejects with error', () => {
  return expect(fetchBadData()).rejects.toThrow('error')
})

Async/Await

test('async/await', async () => {
  const data = await fetchData()
  expect(data).toBe('data')
})
 
test('async error', async () => {
  await expect(fetchBadData()).rejects.toThrow('error')
})

Callbacks

test('callback style', (done) => {
  fetchData((error, data) => {
    try {
      expect(error).toBeNull()
      expect(data).toBe('data')
      done()
    } catch (e) {
      done(e)
    }
  })
})

Timers

jest.useFakeTimers()
 
test('timer-based code', () => {
  const callback = jest.fn()
 
  setTimeout(callback, 1000)
 
  // Fast-forward time
  jest.advanceTimersByTime(1000)
 
  expect(callback).toHaveBeenCalled()
})
 
test('run all timers', () => {
  const callback = jest.fn()
 
  setTimeout(callback, 1000)
  setTimeout(callback, 2000)
 
  jest.runAllTimers()
 
  expect(callback).toHaveBeenCalledTimes(2)
})

Snapshot Testing

Snapshots capture output and compare against future runs.

Basic Snapshots

test('renders correctly', () => {
  const component = render(<Button label="Click me" />)
  expect(component).toMatchSnapshot()
})

Inline Snapshots

test('user object', () => {
  const user = getUser(1)
  expect(user).toMatchInlineSnapshot(`
    Object {
      "id": 1,
      "name": "John",
    }
  `)
})

Updating Snapshots

# Update all snapshots
jest --updateSnapshot
jest -u
 
# Interactive update
jest --watch
# Press 'u' to update failing snapshots
⚠️

Review snapshot changes carefully before committing. Large snapshots can make reviews difficult - consider using inline snapshots or extracting specific assertions for complex objects.

Configuration

jest.config.js

module.exports = {
  // Test environment
  testEnvironment: 'jsdom', // or 'node'
 
  // Test file patterns
  testMatch: ['**/__tests__/**/*.js', '**/*.test.js'],
 
  // Files to ignore
  testPathIgnorePatterns: ['/node_modules/', '/dist/'],
 
  // Module resolution
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1',
    '\\.(css|less)$': 'identity-obj-proxy',
  },
 
  // Setup files
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
 
  // Coverage
  collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}'],
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
    },
  },
 
  // Transform
  transform: {
    '^.+\\.(ts|tsx)$': 'ts-jest',
  },
}

Common CLI Options

# Watch mode
jest --watch
 
# Run specific tests
jest user.test.js
jest --testNamePattern="login"
 
# Coverage
jest --coverage
jest --coverageReporters="html"
 
# Verbose output
jest --verbose
 
# Run in band (sequential)
jest --runInBand
 
# Clear cache
jest --clearCache

Best Practices

Organize Tests Alongside Code

src/
├── components/
│   ├── Button/
│   │   ├── Button.tsx
│   │   ├── Button.test.tsx
│   │   └── index.ts
│   └── Form/
│       ├── Form.tsx
│       └── Form.test.tsx
└── utils/
    ├── format.ts
    └── format.test.ts

Descriptive Test Names

// Good - describes behavior
test('returns null when user is not found', () => {})
test('throws ValidationError for invalid email', () => {})
 
// Avoid - vague
test('works correctly', () => {})
test('test 1', () => {})

AAA Pattern

test('calculates total with discount', () => {
  // Arrange
  const cart = new ShoppingCart()
  cart.addItem({ price: 100 })
  cart.applyDiscount(0.1)
 
  // Act
  const total = cart.getTotal()
 
  // Assert
  expect(total).toBe(90)
})

Avoid Test Interdependence

// Bad - tests share state
let counter = 0
test('increments', () => {
  counter++
  expect(counter).toBe(1)
})
test('decrements', () => {
  counter--
  expect(counter).toBe(0)
})
 
// Good - each test is independent
test('increments', () => {
  const counter = new Counter(0)
  counter.increment()
  expect(counter.value).toBe(1)
})

Mock External Dependencies

// Mock fetch for all tests
global.fetch = jest.fn()
 
beforeEach(() => {
  fetch.mockClear()
})
 
test('fetches user', async () => {
  fetch.mockResolvedValue({
    ok: true,
    json: () => Promise.resolve({ id: 1 }),
  })
 
  const user = await getUser(1)
  expect(user.id).toBe(1)
})

Jest's comprehensive feature set makes it ideal for testing modern JavaScript applications. From simple unit tests to complex integration scenarios with mocking and async handling, Jest provides the tools you need while maintaining fast, reliable test execution.

Quiz on Jest

Your Score: 0/10

Question: What is the difference between toBe() and toEqual() in Jest?

Continue Reading

Frequently Asked Questions (FAQs) / People Also Ask (PAA)

How does Jest compare to Mocha or Jasmine?

Can I use Jest with TypeScript?

How do I test React components with Jest?

Why are my tests running slowly?

How do I debug failing tests?

How do I mock environment variables?

What's the difference between mockReturnValue and mockImplementation?

How do I generate and view code coverage?