Software testing is vital for ensuring your applications work as expected, especially when you introduce changes. Catching and fixing errors early in development is crucial for maintaining resilient, high-quality code.

Of the many available tools and frameworks for JavaScript testing, Jest is one of the most popular. A product by Meta, Jest features extensive testing capabilities for JavaScript applications and those built with JavaScript frameworks.

Let’s explore the Jest framework, its features, and how best to integrate it into your development workflow.

What Is Jest?

Jest is a flexible framework and simple to use. In addition to its core JavaScript-testing features, it offers configurations and plugins to support testing Babel, webpack, Vite, Parcel, or TypeScript-based applications.

Jest has seen widespread adoption among developers and boasts an array of community-built and maintained plugins. It stands out for its ease of use: JavaScript testing requires no additional configurations or plugins. But you can also perform more advanced testing — like testing JavaScript frameworks — using some extra configuration options.

How To Set Up Jest for Your JavaScript Project

Let’s explore how to set up Jest in an existing JavaScript project.

Prerequisites

To follow this tutorial, ensure you have the following:

Install the Jest Package

  1. If you don’t already have a project to follow along with this tutorial, use this repo as a starting point.

The starter-files branch gives you a base to build the application as you follow the tutorial. Reference the main branch to view this tutorial’s code and cross-check your code.

  1. To install Jest with npm, go to the project directory in your terminal and run the following command:
npm install --save-dev jest

The --save-dev option tells npm to install the package under devDependencies, which contains the dependencies you need for development.

Configure Jest

Though Jest generally works without additional configuration, there are two ways to expand its power: in the package.json file and via a Jest configuration file.

Configure Jest in package.json

In your package.json file, add an object named jest with properties as shown below:

{
  …
  "jest": {
    "displayName": "Ecommerce",
    "globals": {
      "PROJECT_NAME": "Ecommerce TD"
    },
    "bail": 20,
    "verbose": true
  },
}

During the test, Jest searches this object and applies these configurations. You can view additional options on Jest’s configurations page, but this object’s properties include:

  • displayName — Jest adds this property’s value as a label to your test results.
  • globals — Holds an object value to define global variables available in your test environments.
  • bail — By default, Jest runs through all tests and displays the errors in the results. bail tells Jest to stop running after a set number of failures.
  • verbose — When set to true, this shows individual test reports during the test execution.

Configure Jest in a Configuration File

You can also configure Jest in a jest.config.js file. Jest also supports .ts, .mjs, .cjs, and .json extensions. When executing tests, Jest looks for these files and applies the settings in the file it finds.

For example, consider this jest.config.js file:

const config = {
  displayName: "Ecommerce",
  globals: {
    "PROJECT_NAME": "Ecommerce TD"
  },
  bail: 20,
  verbose: true
}

module.exports = config;

The code exports a Jest configuration object with the same properties as the previous example.

You can also use a custom file that contains a JSON-serializable configuration object and pass the file path to the --config option when executing your tests.

Create a Basic Test File

With Jest configured, create your test files. Jest reviews your project’s test files, executes them, and provides the results. Test files usually follow a format such as [name].test.js or [name]-test.js. This pattern makes it easy for both Jest and your team to identify your test files.

Consider a string-format.js file that has the following code:

function truncate(
  str,
  count,
  withEllipsis = true
) {
  if (str.length < = count)
    return str

  const substring = str.substr(0, count)

  if (!withEllipsis)
    return substring

  return substring + '...'
}

module.exports = { truncate }

The function truncate() truncates strings to a particular length with the option to add an ellipsis.

Write the Test

  1. Create a test file named string-format.test.js.
  1. To keep your files organized, place string-format.test.js in the same directory you have the string-format.js file or in a specific test directory. Regardless of where your test file is within the project, Jest finds and executes it. With Jest, you can test your applications in various scenarios.
  2. Write a basic test in string-format.test.js as follows:
const { truncate } = require('./string-format')

test('truncates a string correctly', () = > {
  expect(truncate("I am going home", 6)).toBe('I am g...')
})

The test case has the description truncates a string correctly. This code uses the expect function provided by Jest, which tests if a value matches the expected outcome.

The code passes truncate("I am going home", 6) as an argument to expect. This code tests the value returned from calling truncate with the arguments "I am going home" and 6. The expect call returns an expectation object, which offers access to Jest matches.

It also contains the toBe matcher, which has "I am g…" as an argument. The toBe matcher tests equality between the expected and actual values.

Execute the Test

To execute your tests, define the jest command.

  1. In your project’s package.json file, add this test script:
"scripts": {
  "test": "jest"
}
  1. Now run npm run test, npm test, or npm t in your terminal. It runs Jest for the project.

When you execute the tests, this is the result:

Jest test result showing passed for the
Successful Jest test result for string-format.test.js.

The results show one test suite (the string-format.test.js file), one test successfully executed ("truncates a string correctly"), and the displayName (Ecommerce) that you defined in the configuration.

  1. In string-format.js, if you add an extra period to break the code and run the test, it fails:

Jest test result showing failed for the
Failed Jest test result for a broken truncate function.

This result suggests that you have broken the truncate function or made updates that require updating the tests.

How To Write Tests With Jest

Jest Test Syntax

Jest’s proprietary syntax is simple to use. Jest exposes global methods and objects to your project for writing tests. Some of its fundamental terms are describe, test, expect, and matchers.

  • describe: This function groups related tests in a file.
  • test: This function runs the test. It’s an alias for it. It contains assertions for the values you want to test.
  • expect: This function declares the assertions for various values. It provides access to matchers for various forms of assertions.
  • Matchers: They let you assert a value in various ways. You can assert value equality, boolean equality, and contextual equality (like whether an array contains the value).

To use them, consider the following example:

  1. Replace the test in the string-format.test.js file with the following code:
describe("all string formats work as expected", () = > {
  test("truncates a string correctly", () = > {
    expect(
      truncate("I am going home", 6)
    ).toBe("I am g...")
  })
})

 

  1. Run the code.

The result looks like the following:

Jest test result showing passed for the
Successful Jest test result showing the describe label.

The screenshot shows that the label in the describe function creates a block. Although describe is optional, grouping the tests in a file with more context is helpful.

Organize Tests in Test Suites

In Jest, a test case consists of the test function, the expect function and a matcher. A collection of related test cases is a test suite. In the earlier example, string-format.test.js is a test suite comprising one test case to test the string-format.js file.

Suppose you have more files in your project, like file-operations.js, api-logger.js, and number-format.js. You can create test suites for these files, like file-operations.test.js, api-logger.test.js, and number-format.test.js.

Write Simple Assertions with Jest Matchers

We’ve explored an example of using the toBe matcher. Assertions with other Jest matchers include:

  • toEqual — For testing “deep” equality in object instances.
  • toBeTruthy — For testing if a value is true in a boolean context.
  • toBeFalsy — For testing if a value is false in a boolean context.
  • toContain — For testing that an array contains a value.
  • toThrow — For testing that an invoked function throws an error.
  • stringContaining — For testing that a string contains a substring.

Let’s explore examples using some of these matchers.

You might, for example, expect a function or code to return an object with specific properties and values.

  1. Use the code snippet below to test this functionality. In this case, you want to assert that the returned object equals an expected object.
expect({
  name: "Joe",
  age: 40
}).toBe({
  name: "Joe",
  age: 40
})

This example uses toBe. The test fails as this matcher doesn’t check for deep equality — it checks the value, not all properties.

  1. Use the toEqual matcher to check for deep equality:
expect({
  name: "Joe",
  age: 40
}).toEqual({
  name: "Joe",
  age: 40
})

This test passes as both objects are “deeply equal,” meaning all their properties are equal.

  1. Try another matcher example that tests if the defined array contains a specific element.
expect(["orange", "pear", "apple"]).toContain("mango")

This test fails because toContain asserts that the ["orange", "pear", "apple"] array contains an expected value "mango", but the array doesn’t.

  1. Use variables for the same test as with the code below:
const fruits = ["orange", "pear", "apple"];
const expectedFruit = "mango";

expect(fruits).toContain(expectedFruit)

Test Asynchronous Code

So far, we’ve tested synchronous code — expressions that return a value before the code executes the following line. You can also use Jest for asynchronous code with async, await, or Promises.

For example, the apis.js file has a function for making an API request:

function getTodos() {
  return fetch('https://jsonplaceholder.typicode.com/todos/1')
}

The getTodos function sends a GET request to https://jsonplaceholder.typicode.com/todos/1.

  1. Create a file named apis.test.js with the following code to test the fake API:
const { getTodos } = require('./apis')

test("gets a todo object with the right properties", () = > {
  return getTodos()
    .then((response) = > {
      return response.json()
    })
    .then((data) = > {
      expect(data).toHaveProperty('userId')
      expect(data).toHaveProperty('id')
      expect(data).toHaveProperty('title')
      expect(data).toHaveProperty('completed')
      expect(data).toHaveProperty('description')
    })
})

This test case invokes the getTodos function that fetches a todo object. When it resolves the Promise, it uses the .then method to get the resolved value.

In that value, the code returns response.json(), which is another Promise that converts the response to JSON format. Another .then method gets the JSON object containing the expect and matchers. The code asserts that the JSON object includes five properties: userId, id, title, completed, and description.

  1. Execute the tests:

Jest test result showing failed for the
Jest test result showing a failed test for asynchronous code.

As the screenshot shows, the test for getTodos() fails. It expects the description property, but the API doesn’t return it. With this information, you can now ask your company’s API management team to include that property if the application needs it or update the tests to meet the API’s response.

  1. Remove the assertion for the description property and rerun the tests:

Jest test result showing passed for the
Jest test result showing a passed test for asynchronous code.

The screenshot shows that everything passed the test.

  1. Now try using async/await instead of traditional Promise handling:
test("gets a todo object with the right properties", async () = > {
  const response = await getTodos()
  const data = await response.json()

  expect(data).toHaveProperty("userId")
  expect(data).toHaveProperty("id")
  expect(data).toHaveProperty("title")
  expect(data).toHaveProperty("completed")
})

The async keyword is now before the function. The code uses await before getTodos() and await before response.json().

Advanced Jest Features

Mock Functions and Modules

You may want to test an expression with external dependencies when writing tests. In some cases, especially unit testing, your unit tests should be isolated from external effects. In that case, you can mock your functions or modules with Jest to better control your tests.

  1. For example, consider a functions.js file that contains the following code:
function multipleCalls(count, callback) {
  if (count < 0) return;

  for (let counter = 1; counter <= count; counter++) {
    callback()
  }
}

The multipleCalls function is executed based on the value of count. It depends on the callback function — the external dependency. Its purpose is to know whether multipleCalls executes the external dependency correctly.

  1. To mock the external dependency and track the dependency’s state in your test file, functions.test.js, use this code:
const { multipleCalls } = require('./functions')

test("functions are called multiple times correctly", () => {
  const mockFunction = jest.fn()

  multipleCalls(5, mockFunction)

  expect(
    mockFunction.mock.calls.length
  ).toBe(5)
})

Here, the fn method of the jest object creates a mock function. Then, the code executes multipleCalls by passing 5 and the mock function as arguments. Then, it asserts that the mockFunction is called five times. The mock property contains information about how the code calls the function and returned values.

  1. When you run the test, this is the expected result:

Jest test result showing passed for the three tests:
Successful Jest test result with a mock function.

As demonstrated, the code calls the mockFunction five times.

In the code, the mock function imitates an external dependency. It doesn’t matter what the external dependency is when the application uses multipleCalls  in production. Your unit test doesn’t care how the external dependency works. It just verifies that multipleCalls works as expected.

  1. To mock modules, use the mock method and pass a file path, which is the module:
const {
  truncate,
} = require("./string-format")

jest.mock("./string-format.js")

This code imitates all functions that string-format.js exports and tracks how often it calls them. The module’s truncate becomes a mock function, which causes the function to lose its original logic. You can find out how many times truncate executes in your tests in the truncate.mock.calls.length property.

If you have an error or your code doesn’t work, compare your code with the complete implementation.

Test React Components With Jest and React Testing Library

If you don’t already have a project to follow along with this tutorial, you can use this React example project as a starting point. The starter-files branch helps you start composing the code as you follow the tutorial. Use the main branch as a reference to cross-check your code against this tutorial’s complete code.

You can use Jest to test JavaScript frameworks such as React. When you create React projects using Create React App, they support React Testing Library and Jest out of the box. If you create a React project without Create React App, install Jest to test React with Babel and the React testing library. If you clone the starter-app branch, you don’t need to install dependencies or apply configurations.

  1. If you are using the example project, use this command to install the required dependencies:
npm install --save-dev babel-jest @babel/preset-env @babel/preset-react react-testing-library

You can also use Enzyme instead of React Testing Library.

  1. Update your Babel configurations in babel.config.js or create this file if it doesn’t exist:
module.exports = {
  presets: [
    '@babel/preset-env',
      ['@babel/preset-react', {runtime: 'automatic'}],
  ],
};
  1. Consider the src/SubmitButton.js file that has the following code:
import React, { useState } from 'react'

export default function SubmitButton(props) {
  const {id, label, onSubmit} = props
  const [isLoading, setisLoading] = useState(false)

  const submit = () => {
    setisLoading(true)
    onSubmit()
  }

  return 

This SubmitButton component receives three props:

  • id — The button’s identifier.
  • label — What text to render in the button.
  • onSubmit — What function to trigger when someone clicks the button.

The code assigns the id prop to the data-testid attribute, which identifies an element for testing.

The component also tracks the isLoading state and updates it to true when someone clicks the button.

  1. Create the test for this component. Place the following code in a SubmitButton.test.js file:
import {fireEvent, render, screen} from "@testing-library/react"
import "@testing-library/jest-dom"
import SubmitButton from "./SubmitButton"

test("SubmitButton becomes disabled after click", () => {
  const submitMock = jest.fn()

  render(
    <SubmitButton
      id="submit-details"
      label="Submit"
      onSubmit={submitMock}
    / >
  )

  expect(screen.getByTestId("submit-details")).not.toBeDisabled()

  fireEvent.submit(screen.getByTestId("submit-details"))

  expect(screen.getByTestId("submit-details")).toBeDisabled()
})

The code above renders the SubmitButton component and uses the screen.getByTestId query method to get the DOM node by the data-testid attribute.

The first expect is getByTestId("submit-details") and uses the not modifier and toBeDisabled matcher (exposed from react-testing-library) to assert that the button isn’t disabled. Use the not modifier with every matcher to assert the matcher’s opposite.

Then, the code fires the submit event on the component and checks that the button is disabled. You can find more custom matchers in the testing library documentation.

  1. Now, run the tests. If you cloned the starter-files branch, ensure you have all the project dependencies installed by running npm install before starting your tests.

Jest test result showing passed for the four tests:
Jest test result showing that a react component test passed.

Run Code Coverage Reports

Jest also offers code coverage reports to show how much of your project you’re testing.

  1. Pass the --coverage option to Jest. In your Jest script in package.json (in the JavaScript project), update the Jest command with this coverage option:
"scripts": {
  "test": "jest --coverage"
}
  1. Run npm run test to test your code. You get a report like the following:

Jest test result showing coverage for the four tests. The result shows that
Successful Jest coverage report for each test suit.

This report shows that Jest tested 100% of the functions in SubmitButton.js and string-format.js. It also indicates that Jest hasn’t tested any statements and lines in string-format.js. The test coverage shows that the uncovered lines in string-format.js are 7 and 12.

On line 7, return str in the truncate function doesn’t execute because the condition if (str.length <= count) returns false.

On line 12, also in the truncate function, the return substring doesn’t execute because the condition if (!withEllipsis) returns false.

Integrate Jest With Your Development Workflow

Let’s see how you can integrate these tests to improve your development workflow.

Run Tests in Watch Mode

Instead of manually executing tests, you can run them automatically when you change your code using watch mode.

  1. To enable watch mode, update your Jest command script in package.json (in the JavaScript project) by adding the --watchAll option:
"scripts": {
  "test": "jest --coverage --watchAll"
}
  1. Run npm run test. It triggers Jest in watch mode:

Jest in watch mode, showing four passed tests, and also a list of commands to use in watch mode.It shows command f, to run only failed tests; o, to run only tests related to changed files; p, to filter by a filename regex pattern; t, to filter by a test name regex pattern; q to quite watch mode; Enter, to trigger a test run.
Running Jest in watch mode.

The tests run every time you change your project. This approach promotes continuous feedback as you build your application.

Set Up Pre-Commit Hooks

In Git environments, hooks execute scripts whenever a particular event happens (such as pull, push, or commit). Pre-commit hooks define which scripts run for the pre-commit event (which the code triggers before making a commit).

The commit only succeeds if the script doesn’t throw an error.

Running Jest before pre-commit ensures that none of your tests fail before committing.

You can use various libraries to set up git hooks in your project, such as ghooks.

  1. Install ghooks under devDependencies:
npm install ghooks --save-dev
  1. Add a configs object in the top level of your package.json file (in the JavaScript project).
  2. Add a ghooks object under configs.
  1. Add a property with a key of pre-commit and a value of jest.
{
  …
  "config": {
    "ghooks": {
      "pre-commit": "jest"
    }
  },
}
  1. Commit the code. The code triggers the pre-commit hook, which executes Jest:

Jest execution during a pre-commit stage. On making a commit using git commit -m on the terminal, Jest is executed and the result of the tests are displayed.
Running Jest during pre-commit using ghooks.

Summary

Now you know how to integrate Jest into your development workflow to automatically execute whenever you make a change. This approach provides continuous feedback so you can quickly fix any code issues before releasing your changes to production.

By hosting your application with Kinsta, you benefit from a fast and secure infrastructure, deploying your projects on infrastructure built on Google Cloud Platform’s Premium Tier network and C2 machines. Choose between 25 data centers and an HTTP/3-enabled CDN.

Stay secure with isolated container technology, two strong firewalls, and advanced Cloudflare-powered DDoS protection. And you can integrate apps or automate workflows with the Kinsta API.

Set up Jest and browse Kinsta’s resources today to improve your JavaScript applications.

Marcia Ramos Kinsta

I'm the Editorial Team Lead at Kinsta. I'm a open source enthusiast and I love coding. With more than 7 years of technical writing and editing for the tech industry, I love collaborating with people to create clear and concise pieces of content and improve workflows.