Skip to content
Jest Testing Framework — Complete Guide with Examples

Jest Testing Framework — Complete Guide with Examples

DodaTech Updated Jun 7, 2026 8 min read

Jest is a JavaScript testing framework developed by Meta that provides a complete test runner, assertion library, mocking utilities, and code coverage — all out of the box with zero configuration for most projects.

What You’ll Learn

By the end of this tutorial, you’ll be able to set up Jest in any JavaScript or TypeScript project, write effective unit tests using matchers and mocks, test asynchronous code, generate coverage reports, and integrate Jest into your CI pipeline.

Why Jest Matters

Jest is the most popular testing framework in the JavaScript ecosystem. It’s the default choice for React applications, used by millions of developers, and maintains a rich plugin ecosystem. At DodaTech, Jest is the backbone of unit testing for Doda Browser’s rendering engine and DodaZIP’s compression algorithms.

Jest Learning Path

    flowchart LR
  A[Testing Basics] --> B[Jest]
  B --> C[Testing Library]
  B --> D[Playwright]
  B --> E[Cypress]
  B --> F{You Are Here}
  style F fill:#f90,color:#fff
  
Prerequisites: Familiarity with JavaScript (ES6+ syntax) or TypeScript. Basic testing concepts from the Testing Basics tutorial are helpful but not required.

Setting Up Jest

Create a new project and install Jest:

mkdir jest-demo && cd jest-demo
npm init -y
npm install --save-dev jest

Add the test script to package.json:

{
  "scripts": {
    "test": "jest"
  }
}

For TypeScript projects, add ts-jest:

npm install --save-dev jest ts-jest @types/jest typescript
npx ts-jest config:init

Create your first test file:

// sum.test.js
const sum = (a, b) => a + b;

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

Expected output:

PASS  ./sum.test.js
  ✓ adds 1 + 2 to equal 3 (2 ms)

Matchers

Matchers are methods on the expect object that assert values in different ways.

Common Matchers

test('common matchers', () => {
  // Equality
  expect(2 + 2).toBe(4);
  expect({ name: 'Alice' }).toEqual({ name: 'Alice' });

  // Truthiness
  expect(null).toBeNull();
  expect(undefined).toBeUndefined();
  expect(1).toBeDefined();
  expect(true).toBeTruthy();
  expect(0).toBeFalsy();

  // Numbers
  expect(10).toBeGreaterThan(5);
  expect(10).toBeLessThanOrEqual(10);
  expect(0.1 + 0.2).toBeCloseTo(0.3);

  // Strings
  expect('hello world').toMatch(/world/);
  expect('hello world').toContain('world');

  // Arrays
  expect([1, 2, 3]).toContain(2);
  expect([1, 2, 3]).toHaveLength(3);

  // Objects
  expect({ a: 1, b: 2 }).toMatchObject({ a: 1 });
  expect({ a: 1 }).toHaveProperty('a');
});

Expected output:

PASS  ./matchers.test.js
  ✓ common matchers (3 ms)

Testing Asynchronous Code

Jest handles promises, async/await, and callbacks natively.

// asyncOperations.js
function fetchUser(id) {
  return Promise.resolve({ id, name: 'Alice' });
}

function fetchUsers() {
  return Promise.reject(new Error('Network error'));
}

// asyncOperations.test.js
const { fetchUser, fetchUsers } = require('./asyncOperations');

test('fetchUser returns user by id', async () => {
  const user = await fetchUser(1);
  expect(user.name).toBe('Alice');
});

test('fetchUser resolves with correct id', () => {
  return expect(fetchUser(2)).resolves.toHaveProperty('id', 2);
});

test('fetchUsers rejects with error', async () => {
  await expect(fetchUsers()).rejects.toThrow('Network error');
});

Expected output:

PASS  ./asyncOperations.test.js
  ✓ fetchUser returns user by id (3 ms)
  ✓ fetchUser resolves with correct id (1 ms)
  ✓ fetchUsers rejects with error (2 ms)

Mocks and Spies

Jest mocks let you replace real implementations with controlled test doubles.

// paymentService.js
const api = require('./api');

async function processPayment(userId, amount) {
  const user = await api.getUser(userId);
  if (user.balance < amount) throw new Error('Insufficient funds');
  return api.charge(userId, amount);
}

// paymentService.test.js
const api = require('./api');
const { processPayment } = require('./paymentService');

jest.mock('./api');

test('processPayment charges user when balance is sufficient', async () => {
  api.getUser.mockResolvedValue({ id: 1, balance: 100 });
  api.charge.mockResolvedValue({ success: true, transactionId: 'txn_123' });

  const result = await processPayment(1, 50);

  expect(result.success).toBe(true);
  expect(api.charge).toHaveBeenCalledWith(1, 50);
  expect(api.charge).toHaveBeenCalledTimes(1);
});

test('processPayment throws when balance is insufficient', async () => {
  api.getUser.mockResolvedValue({ id: 1, balance: 10 });

  await expect(processPayment(1, 50)).rejects.toThrow('Insufficient funds');
  expect(api.charge).not.toHaveBeenCalled();
});

Expected output:

PASS  ./paymentService.test.js
  ✓ processPayment charges user when balance is sufficient (4 ms)
  ✓ processPayment throws when balance is insufficient (2 ms)

Code Coverage

Jest includes built-in code coverage via the --coverage flag:

npx jest --coverage

This generates a terminal report and an HTML report in the coverage/ directory showing:

  • % Stmts: percentage of statements covered
  • % Branch: percentage of branches (if/else, switch) covered
  • % Funcs: percentage of functions called
  • % Lines: percentage of executable lines covered

The Jest Testing Flow

    flowchart LR
  A[Write Test] --> B[Run jest]
  B --> C{Pass?}
  C -->|Yes| D[Refactor]
  C -->|No| E[Fix Code]
  E --> B
  D --> A
  

Common Jest Mistakes

1. Forgetting to Return or Await a Promise

If a test function returns a promise (like an async function), Jest waits for it to resolve. If you forget the await, the test passes before the assertion runs.

Fix: Always await async assertions or return the promise explicitly.

2. Using toBe for Objects and Arrays

toBe uses Object.is — it’s reference equality, not deep equality. Two objects with the same properties are different references.

Fix: Use toEqual or toStrictEqual for comparing objects and arrays.

3. Mocks Persisting Between Tests

If a mock’s implementation or return value bleeds from one test into the next, tests become interdependent.

Fix: Use beforeEach to clear mocks: jest.clearAllMocks() or jest.resetAllMocks().

4. Testing Too Much in One Test

A single test that checks five different behaviors makes failures hard to diagnose.

Fix: One assertion or behavior per test. If you have multiple expectations about related state, use describe to group them.

5. Not Using describe Blocks

Flat test files with 20+ tests become hard to navigate and read.

Fix: Group related tests with describe blocks.

6. Mocking Modules Incorrectly

Jest’s hoisting of jest.mock calls can cause confusing errors. The call is hoisted to the top of the file, but the variable assignment isn’t.

Fix: Place jest.mock calls at the top of the file, outside any describe or test blocks.

7. Ignoring the Coverage Report

Coverage below 80% on critical modules indicates untested paths.

Fix: Run --coverage regularly and review uncovered lines.

Practice Questions

1. How do you test that a function throws an error in Jest?

Wrap the function call in a function: expect(() => fn()).toThrow('error message'). For async functions: await expect(fn()).rejects.toThrow('error').

2. What’s the difference between toBe and toEqual?

toBe checks reference equality (same object in memory). toEqual checks deep equality (same properties and values).

3. How do you mock a module in Jest?

Call jest.mock('./module') at the top of the test file. Then access the mock via require('./module') and use .mockResolvedValue(), .mockImplementation(), etc.

4. How do you run only one test file?

npx jest path/to/test-file.test.js or use .only on a test: test.only('name', fn).

5. Challenge: Write tests for an array utility library.

Implement and test: chunk(array, size) splits an array into groups of size, unique(array) removes duplicates, and flatten(array) flattens one level.

Mini Project: Jest Test Suite for a Task Queue

// taskQueue.js
class TaskQueue {
  constructor() {
    this.tasks = [];
    this.running = false;
  }

  add(task) {
    this.tasks.push(task);
    return this.tasks.length;
  }

  async runAll() {
    this.running = true;
    const results = [];
    while (this.tasks.length > 0) {
      const task = this.tasks.shift();
      results.push(await task());
    }
    this.running = false;
    return results;
  }

  get pending() {
    return this.tasks.length;
  }

  get isRunning() {
    return this.running;
  }
}

module.exports = TaskQueue;

// taskQueue.test.js
const TaskQueue = require('./taskQueue');

describe('TaskQueue', () => {
  let queue;

  beforeEach(() => {
    queue = new TaskQueue();
  });

  test('starts empty', () => {
    expect(queue.pending).toBe(0);
    expect(queue.isRunning).toBe(false);
  });

  test('adds tasks and returns count', () => {
    const count = queue.add(() => Promise.resolve(1));
    expect(count).toBe(1);
    expect(queue.pending).toBe(1);
  });

  test('runs all tasks and returns results in order', async () => {
    queue.add(() => Promise.resolve('a'));
    queue.add(() => Promise.resolve('b'));
    const results = await queue.runAll();
    expect(results).toEqual(['a', 'b']);
    expect(queue.pending).toBe(0);
  });

  test('sets running state during execution', async () => {
    queue.add(() => Promise.resolve(1));
    const runPromise = queue.runAll();
    expect(queue.isRunning).toBe(true);
    await runPromise;
    expect(queue.isRunning).toBe(false);
  });
});

Expected output:

PASS  ./taskQueue.test.js
  TaskQueue
    ✓ starts empty (2 ms)
    ✓ adds tasks and returns count (1 ms)
    ✓ runs all tasks and returns results in order (3 ms)
    ✓ sets running state during execution (2 ms)

FAQ

How is Jest different from Mocha or Jasmine?
Jest is an all-in-one framework (runner + assertions + mocks + coverage). Mocha requires separate libraries (Chai for assertions, Sinon for mocks). Jasmine is similar to Jest but lacks snapshot testing and built-in TypeScript support.
Can I use Jest with TypeScript?
Yes. Install ts-jest and @types/jest, then configure Jest to use ts-jest as the transform for .ts files. Alternatively, use @swc/jest for faster compilation.
How do I test a React component with Jest?
Jest pairs with Testing Library. Use render from @testing-library/react and screen.getByText to verify component output. Snapshot testing is also available via expect(container).toMatchSnapshot().
What’s the difference between jest.mock and jest.spyOn?
jest.mock replaces the entire module with mock implementations. jest.spyOn wraps an existing method and tracks calls while preserving the original implementation (unless you call mockImplementation on it).
How do I debug a failing test?
Use --verbose for detailed output, --runInBand to run tests sequentially, and --detectOpenHandles to find unresolved promises. For deep debugging, add debugger statements and run with node --inspect-brk with Jest’s --runInBand flag.

Try It Yourself

  1. Create a new Node.js project and install Jest
  2. Write a test for a simple utility function (like capitalize(str))
  3. Run npx jest --watch to watch files for changes
  4. Add a mock and verify it was called correctly
  5. Run with --coverage and view the HTML report

What’s Next

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro