Jest: The Comprehensive JavaScript Testing Framework

Jest: The Comprehensive JavaScript Testing Framework

Introduction

Jest is a JavaScript Testing Framework known for its simplicity and ease of use. It is particularly favoured in projects utilising React, Angular, Vue, Node.js, Babel, and TypeScript.

This post is designed to provide a ‘mini-manual’ to setting up and efficiently utilising Jest in your JavaScript projects, covering everything from basic setup to advanced features.

1. Getting Started

Installation

To begin, ensure Node.js is installed on your system. If you’re starting a new project, initialise it using npm init. Install Jest using npm by running the command:

npm install --save-dev jest

This command adds Jest as a development dependency in your project.

Configuration

In your package.json, add a script to facilitate running tests with Jest. Under scripts, add the test script as follows:

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

This setup allows you to run your Jest tests using npm test.

Initial Test

Create a file named example.test.js. In this file, write a simple test to ensure Jest is set up correctly:

test('true is true', () => { expect(true).toBe(true); });

Run npm test and observe the output. If everything is set up correctly, Jest will run the test and report success.

2. Writing Tests

Basic Test Structure

Jest tests typically reside in files named *.test.js or *.spec.js. The basic structure of a Jest test involves describe and test (or it) blocks. The describe block groups together several related tests, and the test block contains an individual test case.

Example of Test Suite

Here’s an example of a simple test suite for a hypothetical math utility module:

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

describe('math operations', () => {
  test('adds two numbers', () => {
    expect(mathUtils.add(1, 2)).toBe(3);
  });

  test('subtracts two numbers', () => {
    expect(mathUtils.subtract(5, 2)).toBe(3);
  });
});

In this example, describe groups the tests for addition and subtraction, while each test block contains a specific case.

Test Naming

Choose clear, descriptive names for your test cases and suites. This practice makes it easier to understand what each test is verifying and aids in debugging when a test fails.

3. Running Tests

Basic Test Execution

To run your tests, simply execute npm test in your project’s root directory. Jest automatically finds and runs all files with .test.js or .spec.js suffixes in your project.

Watching for Changes

While developing, you can run Jest in watch mode. This mode re-runs tests when it detects changes in your files. Run Jest in watch mode using:

npm test -- --watch

This mode boosts productivity by providing immediate feedback as you update your code and tests.

Filtering Test Runs

In larger projects, you might not want to run all tests every time. Jest allows you to specify a pattern or a filename to run a subset of your tests. For example:

npm test -- mathUtils

This command will only run tests in files that include mathUtils in their filename.

4. Mocking

Purpose of Mocking

Mocking in testing is crucial for isolating the unit of code you’re testing. It replaces complex, unpredictable, or external parts of your application with simplified and controllable versions.

Creating Mock Functions

Use jest.fn() to create a mock function. You can specify a return value or implement a mock implementation. For example:

const myMock = jest.fn(); myMock.mockReturnValue('Hello World');

This mock function, when called, will always return ‘Hello World’.

Mocking Modules

Jest allows you to mock entire modules, which is particularly useful for dependencies like APIs or databases. For instance:

jest.mock('axios');

This line replaces the axios HTTP client module with a mock version for your tests.

5. Asynchronous Testing

Handling Async Code

Testing asynchronous code requires special consideration since you need to ensure Jest waits for the async code to complete. Jest provides several methods to handle this.

Callbacks

For traditional callback-based asynchronous code, Jest provides the done function. Call done when your async test is complete. For example:

test('async test with callback', done => {
  setTimeout(() => {
    expect(true).toBe(true);
    done();
  }, 1000);
});

In this test, Jest waits for the done callback before considering the test complete.

Promises

If your code uses Promises, return the Promise from your test. Jest will wait for the Promise to resolve or reject. Here’s an example:

test('async test with promise', () => {
  return fetchData().then(data => {
    expect(data).toBe('expected data');
  });
});

Jest will wait for the fetchData promise to resolve before completing the test.

Async/Await

For modern JavaScript code, use async/await for cleaner asynchronous tests. Mark your test function as async and await the asynchronous operations. For example:

test('async test with async/await', async () => {
  const data = await fetchData();
  expect(data).toBe('expected data');
});

This approach is clean and easy to read, making your asynchronous tests more straightforward.

6. Using Matchers

Introduction to Matchers

Matchers are methods provided by Jest to test values in different ways. They are used with expect to form readable and expressive assertions.

Common Matchers

  • .toBe(value): Tests exact equality.
  • .toEqual(value): Tests value equality, useful for objects and arrays.
  • .toContain(item): Checks if an array or string contains a specific item.
  • .toBeTruthy(): Checks if a value is truthy.

Example Usage

Here’s how you might use these matchers in a test:

test('test object equality', () => {
  const obj = { a: 1, b: 2 };
  expect(obj).toEqual({ a: 1, b: 2 });
});

This test uses .toEqual to check if obj matches the expected object.

7. Configuration and Setup

jest.config.js

For more complex setups, you might need a Jest configuration file. Create a file named jest.config.js in your project’s root. This file allows you to configure Jest extensively to suit your project’s needs.

Example Configuration

Here’s an example of what your jest.config.js might look like:

module.exports = {
  verbose: true,
  setupFilesAfterEnv: ['./jest.setup.js'],
  moduleNameMapper: {
    '^@components/(.*)$': '<rootDir>/src/components/$1',
  },
  testEnvironment: 'node',
};

In this configuration:

  • verbose: true makes Jest output more detailed information during runs.
  • setupFilesAfterEnv specifies scripts that run after the test environment is set up.
  • moduleNameMapper helps Jest understand module aliases, particularly useful in projects with complex directory structures.
  • testEnvironment specifies the environment in which the tests are run (e.g., a browser-like environment or a Node.js environment).

8. Advanced Features

Snapshot Testing

Snapshot testing is a Jest feature that captures the rendered output of components, enabling you to ensure your UI does not change unexpectedly. When you run a snapshot test, Jest creates a file with the rendered output. Subsequent test runs compare the new output to the stored snapshot.

Coverage Reports

Jest can generate coverage reports, showing which parts of your codebase are covered by tests. Enable this feature by adding --coverage to your test script in package.json:

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

The coverage report helps identify untested parts of your code.

Interactive Watch Mode

Jest’s watch mode is an interactive testing mode that automatically re-runs relevant tests as you make changes to your code. It’s an efficient way to get immediate feedback on your changes.

9. Troubleshooting

Common Issues

  • Tests Not Running: Ensure your test files are named correctly and placed in the right directories.
  • Mocking Issues: Double-check your mock configurations and ensure they match the modules you’re trying to mock.
  • Asynchronous Test Failures: Make sure you’re correctly handling asynchronous operations in your tests using callbacks, promises, or async/await.

Seeking Help

For issues not covered here, refer to Jest’s comprehensive documentation. Community forums and Q&A platforms like Stack Overflow are also great resources for specific problems.

10. Further Resources

  • Jest Official Documentation: Jest Documentation offers in-depth guides, API references, and tutorials.
  • Community Resources: Explore blog posts, tutorials, and videos for more practical insights and advanced use cases.

Summary

The list above cover most of the things I use on a regular basis. There are so many features that I will likely update this post from time to time as I need to be reminded of them.

Stephen

Hi, my name is Stephen Finchett. I have been a software engineer for over 30 years and worked on complex, business critical, multi-user systems for all of my career. For the last 15 years, I have been concentrating on web based solutions using the Microsoft Stack including ASP.Net, C#, TypeScript, SQL Server and running everything at scale within Kubernetes.