# Testing JavaScript with Jest

> Learn how to test JavaScript with Jest, from your first test and common matchers to async tests, setup, mocks, fake timers, and snapshots.

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2018-05-17 | Updated: 2026-07-18 | Topics: [DevTools](https://flaviocopes.com/tags/devtool/) | Canonical: https://flaviocopes.com/jest/

Jest is a JavaScript testing framework maintained by the Jest contributors under the OpenJS Foundation.

It includes a test runner, assertions, mocks, code coverage, and snapshot testing. You can use it for Node.js code, browser-oriented code, React applications, and many other JavaScript projects.

## Install Jest

Install Jest as a development dependency:

```bash
npm install --save-dev jest
```

Add a test script to `package.json`:

```json
{
  "scripts": {
    "test": "jest"
  }
}
```

Run the tests:

```bash
npm test
```

Keep Jest local to the project. This makes the version reproducible for other developers and for continuous integration.

Generate a configuration file when you need one:

```bash
npm init jest@latest
```

Many projects can start without custom configuration.

## Write your first test

Create `math.js`:

```js
function sum(a, b) {
  return a + b
}

module.exports = {
  sum,
}
```

Then create `math.test.js`:

```js
const { sum } = require('./math')

test('adds two numbers', () => {
  expect(sum(2, 3)).toBe(5)
})
```

Jest finds files with common names such as `*.test.js` and `*.spec.js`.

`test()` describes one behavior. `expect()` receives the actual value, and the matcher checks it.

This guide uses CommonJS to keep the setup small. ECMAScript modules and TypeScript need configuration that depends on your project. Follow Jest's current setup guide instead of copying configuration from an unrelated stack.

## Use matchers

Use `toBe()` for primitive values or exact object identity:

```js
expect(2 + 2).toBe(4)
```

Use `toEqual()` to compare object or array contents:

```js
expect({
  name: 'Flavio',
  active: true,
}).toEqual({
  name: 'Flavio',
  active: true,
})
```

Other useful matchers include:

- `toBeNull()`
- `toBeUndefined()`
- `toBeTruthy()`
- `toBeFalsy()`
- `toContain()`
- `toHaveLength()`
- `toHaveProperty()`
- `toMatch()`
- `toBeCloseTo()`
- `toThrow()`

Negate a matcher with `not`:

```js
expect(sum(2, 3)).not.toBe(6)
```

Test thrown errors by passing a function to `expect()`:

```js
expect(() => {
  throw new Error('Invalid total')
}).toThrow('Invalid total')
```

## Group related tests

Use `describe()` to group related behavior:

```js
describe('sum', () => {
  test('adds positive numbers', () => {
    expect(sum(2, 3)).toBe(5)
  })

  test('adds negative numbers', () => {
    expect(sum(-2, -3)).toBe(-5)
  })
})
```

Groups make reports easier to read. They also give setup and teardown hooks a clear scope.

## Setup and teardown

Run code before or after every test:

```js
beforeEach(() => {
  // create fresh test data
})

afterEach(() => {
  // restore changed state
})
```

Run code once for the file or `describe()` block:

```js
beforeAll(() => {
  // start a shared resource
})

afterAll(() => {
  // close the shared resource
})
```

Prefer fresh state for each test. Tests that depend on an earlier test's side effects become difficult to run alone.

Hooks can return promises or be `async`.

## Test asynchronous code

Make the test function `async` and await the result:

```js
async function uppercase(value) {
  return value.toUpperCase()
}

test('converts text to uppercase', async () => {
  const result = await uppercase('hello')

  expect(result).toBe('HELLO')
})
```

Test resolved and rejected promises directly:

```js
test('resolves with uppercase text', async () => {
  await expect(uppercase('hello')).resolves.toBe('HELLO')
})
```

```js
test('rejects invalid input', async () => {
  await expect(loadUser('missing')).rejects.toThrow('User not found')
})
```

Always return or await the promise. Otherwise, Jest can finish the test before the assertion runs.

For a callback API, use the `done` argument:

```js
test('returns a user', (done) => {
  loadUserWithCallback(42, (error, user) => {
    if (error) {
      done(error)
      return
    }

    try {
      expect(user.name).toBe('Flavio')
      done()
    } catch (error) {
      done(error)
    }
  })
})
```

Do not both return a promise and use `done` in the same test.

## Create mock functions

`jest.fn()` creates a function that records its calls:

```js
test('notifies the subscriber', () => {
  const subscriber = jest.fn()

  publish('New lesson', subscriber)

  expect(subscriber).toHaveBeenCalledTimes(1)
  expect(subscriber).toHaveBeenCalledWith('New lesson')
})
```

Give a mock a return value:

```js
const getPrice = jest.fn().mockReturnValue(20)

expect(getPrice()).toBe(20)
```

Mocks are useful at a boundary such as email, a clock, or a network client. Do not mock every internal function. Tests tied to implementation details break during harmless refactoring.

## Spy on an object method

`jest.spyOn()` wraps an existing method:

```js
test('writes an audit message', () => {
  const logger = {
    write(message) {
      return message
    },
  }

  const spy = jest.spyOn(logger, 'write')

  logger.write('Account updated')

  expect(spy).toHaveBeenCalledWith('Account updated')

  spy.mockRestore()
})
```

Restore spies after the test. You can also enable Jest's `restoreMocks` configuration option when every test should start with restored mocks.

## Mock time

Fake timers let a test control `setTimeout()` and `setInterval()`:

```js
test('saves after one second', () => {
  jest.useFakeTimers()

  const save = jest.fn()
  setTimeout(save, 1000)

  jest.advanceTimersByTime(1000)

  expect(save).toHaveBeenCalled()

  jest.useRealTimers()
})
```

Use fake timers only when time is part of the behavior. Real timers are easier when a test does not need scheduling control.

## Test browser code

Jest's default environment is Node.js.

Install the jsdom environment when code needs browser DOM APIs:

```bash
npm install --save-dev jest-environment-jsdom
```

Then configure it:

```js
module.exports = {
  testEnvironment: 'jsdom',
}
```

jsdom simulates many browser APIs. It is not a real browser, so use browser-level tests for layout, rendering, and behavior that depends on a full browser engine.

## Snapshot testing

A snapshot stores a serialized value and compares it on later runs:

```js
test('formats a user', () => {
  const user = {
    name: 'Flavio',
    role: 'author',
  }

  expect(user).toMatchInlineSnapshot(`
    {
      "name": "Flavio",
      "role": "author",
    }
  `)
})
```

Snapshots work best for small, stable output that reviewers can understand.

Do not update snapshots automatically just to make a failing test pass. Read the difference and confirm that the new output is intended.

For behavior that matters, a focused matcher often explains the requirement better than a large snapshot.

See the official Jest guides for [getting started](https://jestjs.io/docs/getting-started), [matchers](https://jestjs.io/docs/using-matchers), [asynchronous tests](https://jestjs.io/docs/asynchronous), [mock functions](https://jestjs.io/docs/mock-functions), and [snapshot testing](https://jestjs.io/docs/snapshot-testing).
