Unit Testing with Jest

Testing is one of the most critical parts of software development, especially when working on scalable React applications. Unit testing ensures that individual pieces of your code — such as functions, components, and hooks — behave as expected. It helps developers catch bugs early, maintain consistency, and refactor confidently without breaking existing functionality.

Jest is one of the most popular testing frameworks in the JavaScript ecosystem. It is fast, simple to configure, and widely used for testing React applications. It provides powerful features such as assertions, mocks, spies, and snapshots that make testing efficient and comprehensive.

In this post, we’ll dive deep into unit testing with Jest, covering setup, writing tests, mocking functions, using snapshots, and best practices for real-world React projects.


1. What Is Jest?

Jest is a JavaScript testing framework developed by Facebook. It is designed primarily for testing JavaScript and React-based applications.

Jest focuses on simplicity, providing zero-configuration setup for many JavaScript projects. It integrates seamlessly with React applications created using Create React App (CRA), and it also supports advanced features like test coverage, snapshot testing, and mocking built-in or custom modules.

Key Features of Jest

  • Zero configuration setup with Create React App.
  • Fast test runner that parallelizes tests.
  • Built-in mocking and spying tools.
  • Snapshot testing for React components.
  • Code coverage reporting out of the box.
  • Powerful assertion library using expect().

2. Why Use Jest for React Applications

React applications heavily rely on small, reusable components. Unit testing ensures each component works correctly in isolation before being integrated into a larger system.

Jest is especially suited for React for several reasons:

  1. Ease of use — works out of the box with minimal setup.
  2. Integration with React Testing Library — makes component testing easier.
  3. Mocking and spying tools — isolate code under test easily.
  4. Snapshot testing — verify UI changes automatically.
  5. Performance — runs tests in parallel to reduce runtime.

3. Setting Up Jest

You can set up Jest in any React project in just a few steps.

3.1 Installing Jest

If you’re using Create React App (CRA), Jest is already included. You can directly use the npm test command.

If you’re setting up a React project manually or using another bundler (like Vite or Webpack), install Jest manually:

npm install --save-dev jest

or with Yarn:

yarn add --dev jest

3.2 Creating a Test Script

Add a test command to your package.json:

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

Now, you can run your tests with:

npm test

or

yarn test

3.3 Directory Structure

It’s good practice to keep your test files near the components or modules they test. For example:

src/
  components/
Button.js
Button.test.js
utils/
sum.js
sum.test.js

This structure makes it easier to maintain and locate tests.


4. Writing Unit Tests with Jest

Unit tests are designed to verify that individual units of code behave correctly. In React, a unit can be a component, a utility function, or even a hook.

Let’s start with a simple example.

4.1 Writing Your First Test

Create a file named sum.js:

function sum(a, b) {
  return a + b;
}

module.exports = sum;

Now, create a corresponding sum.test.js file:

const sum = require('./sum');

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

4.2 Running the Test

Run the following command:

npm test

You’ll see an output like this:

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

This confirms your first Jest test passed successfully!


5. Understanding Jest Test Syntax

Let’s break down what happens in the above test:

  • test(description, callback) – defines an individual test.
  • expect(value) – creates an expectation for a given value.
  • .toBe(expected) – an assertion method that checks for equality using ===.

Common Assertion Methods

MethodDescription
.toBe(value)Checks for exact equality
.toEqual(object)Checks deep equality for objects/arrays
.toBeTruthy()Checks if the value is true in a boolean context
.toBeFalsy()Checks if the value is false in a boolean context
.toContain(item)Verifies an array contains a given element
.toThrow(error)Verifies that a function throws an error

Example: Object and Array Testing

test('object assignment', () => {
  const data = { one: 1 };
  data['two'] = 2;
  expect(data).toEqual({ one: 1, two: 2 });
});

6. Testing React Components

Jest can be used along with React Testing Library (RTL) to test React components.

Install React Testing Library:

npm install --save-dev @testing-library/react @testing-library/jest-dom

Example: Button Component

// Button.js
import React from 'react';

export default function Button({ label, onClick }) {
  return <button onClick={onClick}>{label}</button>;
}

Example Test File

// Button.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';

test('renders the button with a label', () => {
  render(<Button label="Click Me" />);
  expect(screen.getByText('Click Me')).toBeInTheDocument();
});

test('calls onClick when clicked', () => {
  const handleClick = jest.fn();
  render(<Button label="Click" onClick={handleClick} />);
  fireEvent.click(screen.getByText('Click'));
  expect(handleClick).toHaveBeenCalledTimes(1);
});

This verifies that:

  1. The button renders with the correct label.
  2. Clicking it triggers the provided event handler.

7. Mocking Functions in Jest

Mocking is the process of replacing real functions or modules with fake ones to isolate components and test them independently.

Jest provides built-in tools like jest.fn() and jest.mock() for creating mocks.

7.1 Mocking a Simple Function

const mockFunction = jest.fn();
mockFunction.mockReturnValue(42);

test('mock function returns 42', () => {
  expect(mockFunction()).toBe(42);
});

7.2 Mocking External Modules

If a component or function depends on an external module, you can mock that dependency to avoid making real API calls or performing expensive operations.

jest.mock('./api', () => ({
  fetchData: jest.fn(() => Promise.resolve({ data: 'mock data' })),
}));

const { fetchData } = require('./api');

test('fetchData returns mock data', async () => {
  const result = await fetchData();
  expect(result.data).toBe('mock data');
});

8. Snapshot Testing

Snapshot testing helps you track changes in React component output over time. It captures a “snapshot” of a component’s rendered output and compares it with the stored version during future test runs.

Example

import React from 'react';
import renderer from 'react-test-renderer';
import Button from './Button';

test('Button snapshot test', () => {
  const tree = renderer.create(<Button label="Click Me" />).toJSON();
  expect(tree).toMatchSnapshot();
});

The first time this test runs, Jest creates a snapshot file inside a __snapshots__ folder. When the component changes, Jest will alert you if the rendered output no longer matches the snapshot, allowing you to review and update it.


9. Testing Asynchronous Code

APIs, network calls, and promises are common in React apps. Jest provides tools for testing asynchronous logic effectively.

Example Using async/await

function fetchData() {
  return Promise.resolve('Hello Jest');
}

test('async fetchData returns data', async () => {
  const data = await fetchData();
  expect(data).toBe('Hello Jest');
});

Example Using Mocks for Async Functions

const mockApi = jest.fn().mockResolvedValue('data');

test('mock async API call', async () => {
  const result = await mockApi();
  expect(result).toBe('data');
  expect(mockApi).toHaveBeenCalledTimes(1);
});

10. Testing React Hooks

React hooks can also be tested with Jest in combination with React Testing Library’s renderHook function from @testing-library/react-hooks.

Example: useCounter Hook

import { useState } from 'react';

export function useCounter() {
  const [count, setCount] = useState(0);
  const increment = () => setCount(count + 1);
  return { count, increment };
}

Example Test

import { renderHook, act } from '@testing-library/react-hooks';
import { useCounter } from './useCounter';

test('increments counter', () => {
  const { result } = renderHook(() => useCounter());
  act(() => {
result.current.increment();
}); expect(result.current.count).toBe(1); });

11. Code Coverage Reports

Jest can generate code coverage reports that show which parts of your application are tested and which are not.

Run:

npm test -- --coverage

You’ll see a detailed coverage summary showing percentages for statements, branches, functions, and lines.


12. Organizing Tests for Maintainability

For larger React projects, organization becomes critical.

Recommended Practices

  1. Group tests logically (per module or component).
  2. Use descriptive test names (what the test checks, not how).
  3. Avoid testing implementation details — focus on behavior.
  4. Use mocks for side effects — network requests, timers, etc.
  5. Run tests automatically in CI/CD pipelines.

13. Debugging Failing Tests

When tests fail, Jest provides clear output showing where and why the failure occurred. You can also add console.log() or debugger statements to inspect intermediate results.

Run Jest in watch mode for faster debugging:

npm test -- --watch

14. Best Practices for Writing Effective Tests

  1. Write small, focused tests – Each test should check one piece of functionality.
  2. Test behavior, not implementation – Don’t rely on internal logic; test outcomes.
  3. Use mocks wisely – Avoid over-mocking, but mock side effects when necessary.
  4. Keep tests independent – Don’t rely on shared state between tests.
  5. Use descriptive names – Clearly state what each test verifies.
  6. Run tests often – Integrate Jest into your CI/CD pipeline for continuous feedback.
  7. Measure coverage – Ensure that all critical paths are tested.

15. Integrating Jest with CI/CD

Jest integrates easily with CI/CD tools like GitHub Actions, Jenkins, or GitLab CI.

Example GitHub Actions workflow:

name: Run Jest Tests
on: [push]
jobs:
  test:
runs-on: ubuntu-latest
steps:
  - uses: actions/checkout@v3
  - name: Install dependencies
    run: npm install
  - name: Run tests
    run: npm test -- --coverage

This ensures your tests run automatically on every commit or pull request.


16. Common Jest Commands

CommandDescription
npm testRun all tests
npm test -- --watchRun tests in watch mode
npm test -- --coverageGenerate coverage report
jest <filename>Run a specific test file
jest -t "test name"Run a specific test by name

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *