Tutorial

Testing JavaScript with Jest

Tutorial · Testing

Cover: Testing JavaScript with Jest

Jest bundles a test runner, an assertion library, and a mocking framework into one package with almost no configuration — which is why it became the default for testing JavaScript.

Your first test

// 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)
})
npx jest

Jest automatically finds any file ending in .test.js and runs it — no manual test registration required.

Common matchers

expect(value).toBe(5)              // strict equality
expect(obj).toEqual({ a: 1 })      // deep equality
expect(array).toContain('item')
expect(fn).toThrow()
expect(value).toBeNull()

toBe uses Object.is (fine for primitives, wrong for objects); toEqual recursively compares object contents — mixing these two up is the single most common Jest mistake.

Mocking functions

const fetchUser = jest.fn().mockResolvedValue({ id: 1, name: 'Amara' })

test('fetches a user', async () => {
  const user = await fetchUser()
  expect(user.name).toBe('Amara')
  expect(fetchUser).toHaveBeenCalledTimes(1)
})

A mock replaces a real function (an API call, a database query) with a stand-in you control — the test stays fast and doesn't depend on a real network or database being available.

Grouping related tests

describe('sum()', () => {
  test('adds positive numbers', () => {
    expect(sum(2, 3)).toBe(5)
  })
  test('handles negatives', () => {
    expect(sum(-1, -1)).toBe(-2)
  })
})

describe blocks are purely organizational — they group output in the test report and let you share setup code with beforeEach.

Test behavior, not implementation. A test that checks a function's internal variable names will break every time you refactor, even if the output is still correct. Assert on what a function returns or what it calls, not how it's written inside.

Comments