Jest is a widely used JavaScript testing framework developed to provide a fast, reliable, and feature-rich testing experience.
With Jest, you get out-of-the-box support for test runners, assertions, snapshots, and mocking, making it a good choice for testing Node.js backends, React applications, and anything in between.
Its zero-config setup allows you to start writing tests immediately without additional configuration.
In this section, you'll explore Jest’s core features and learn how to write and run tests effectively.
Prerequisites
Before diving into Jest, ensure you have Node.js installed, preferably the latest LTS version.
This guide also assumes a basic understanding of JavaScript and testing principles.
Step 1 — Setting up the directory
Before you start writing tests, you will set up a proper environment for Jest. This will ensure your project is structured correctly and Jest runs smoothly with modern JavaScript features.
To begin, create a new directory and navigate into it:
Then, initialize the directory as an npm project:
This command creates a package.json file for your project configuration. Now, enable ES Modules support:
Then, install Jest as a development dependency:
After installing Jest, update your package.json file to include a test script:
This ensures Jest runs in ESM mode since Jest's ESM support is still experimental.
Next, create a simple function to test. Save the following file as math.js:
The add() function takes two numbers and returns their sum.
With the function in place, you're now ready to write your first Jest test.
Step 2 — Writing your first test
Now that you have your add() function, it’s time to write a test to verify that it works as expected. Instead of manually running the function and checking results yourself, you’ll use Jest to automate this process.
First, create a directory to organize your tests. Jest commonly uses a __tests__ folder by convention:
Next, create a test file for your math module. Jest recognizes files with .test.js or .spec.js extensions:
In this test, you import only the add function—Jest automatically provides describe, it, and expect globally.
The describe block groups related tests for better organization, while each it function defines a specific test case with a clear description.
The expect function with the toBe matcher verifies that add(1, 2) equals 3.
Now that you have written the test, you’re ready to run it in the next step.
Step 3 — Running your tests
With your test file in place, you can now run Jest to verify that everything works as expected.
To execute all test files in your project, run the following command:
Since the test script in package.json is set to jest, this command is equivalent to:
Jest will automatically detect and execute test files inside the tests directory. If everything is working correctly, you should see output similar to this:
Jest successfully detected and executed the math.test.js file inside the __tests__ directory. The output confirms that the test passed, displaying relevant details such as the number of test suites and tests executed, their pass status, and the total execution time.
Additionally, a warning about the experimental VM Modules feature is shown, along with an estimate of the expected runtime.
By default, Jest runs tests once and exits. However, you can enable watch mode for continuous testing:
In watch mode, Jest monitors your project files and automatically reruns tests when changes are detected. This creates a rapid feedback loop that helps you identify and fix issues immediately while developing.
Step 4 — Test filtering and running specific tests
Running the entire test suite for every small change becomes inefficient as your test suite grows. Jest provides several ways to run only relevant tests, helping you maintain a fast development workflow.
Using .only to focus on specific tests
When you need to concentrate on a particular test or test group, Jest's .only modifier runs just the tests you specify, temporarily ignoring others.
For example, to focus only on the first test in your math module:
When you run the tests, Jest will execute only the test with .only:
Notice that Jest marks the second test as skipped, letting you focus solely on the functionality you're developing or debugging.
You can also use describe.only to run all tests within a specific test suite:
Using .skip to temporarily exclude tests
When a test is temporarily broken, or you want to exclude specific tests, Jest's .skip modifier allows you to skip them without removing or commenting out the code:
The output confirms one skipped test, one passed test, and successful execution of all test suites.
Similarly, you can skip an entire suite with describe.skip:
This approach is beneficial during refactoring when specific tests might temporarily fail, but you still want to maintain your test coverage.
Filtering tests with command line arguments
Jest offers powerful command-line options to filter tests without modifying your code—ideal for CI/CD pipelines or selective testing.
Let's restore the test suite without any filtering:
To run only tests containing specific text in their description, use the -t or --testNamePattern flag:
The double dash -- passes the subsequent arguments to Jest rather than npm. This command runs all tests with "add function" in their description.
You can further narrow your selection with more specific patterns:
You can also filter by file path to run only tests in specific files:
This runs all test files with "math" in their path. For more precise control, specify the full path:
Interactive watch mode filtering
Jest's watch mode provides a convenient interactive interface for filtering tests:
In watch mode, you can access different filtering options by pressing:
pto filter by filename patterntto filter by test name patternfto run only failed testsoto run only tests related to changed files
This menu-driven approach makes it easy to quickly focus on relevant tests during development without remembering specific command-line arguments.
With these filtering capabilities, Jest lets you maintain focus on the specific parts of the codebase you're working on, significantly speeding up the development feedback loop.
Step 5 — Mocking with Jest
Mocking allows you to replace actual implementations with simulated ones, making it possible to test your code in isolation from external dependencies. This approach is useful when testing functions interacting with file systems, databases, or APIs.
In this section, you'll learn how to use Jest's mocking capabilities to test a function that reads from a file without accessing the file system.
First, create an empty text file:
Next, create a fileReader.js file with a function that reads a file's contents:
This function uses the Promise-based fs.readFile to asynchronously read a file's contents and return them as a trimmed string.
Now, create a test file for this function. When working with ES Modules, Jest requires a special approach for mocking:
Unlike CommonJS, ES Modules require a different mocking approach.
Here, jest.unstable_mockModule() replaces the real fs/promises module with a mock version before importing fileReader.js. This ensures that when readFileContent() is executed, it relies on the mocked readFile function instead of making actual file system calls.
The mock implementation of readFile returns a Promise that resolves to "Mocked file content", allowing the test to control the function’s behavior without accessing the file system.
This approach makes the test:
- Eliminates file I/O operations.
- Avoids issues related to file existence or permissions.
- Always returns the expected output.
Running the test should produce output similar to:
Jest provides several helpful assertion methods specifically for mocks:
toHaveBeenCalled()- verifies that a mock function was calledtoHaveBeenCalledWith()- checks that a mock was called with specific argumentstoHaveBeenCalledTimes()- ensures a mock was called an exact number of times
These matchers make your tests more readable and provide better error messages when tests fail.
Step 6 — Using setup and teardown hooks
When writing multiple tests, you'll often need to perform the same setup or cleanup tasks before or after each test. Jest provides a set of powerful hooks to handle these repetitive operations, helping you maintain clean and efficient test suites.
Let's build on our file reader example and incorporate setup and teardown functionality using Jest's built-in hooks. These hooks will ensure our mocks are properly reset and maintained across multiple tests.
Update the fileReader.test.js file to include these hooks:
The beforeEach hook executes before each test in the describe block. Here, it serves two purposes: resetting all mocks to their initial state with jest.clearAllMocks() and configuring the default behavior for our readFile mock. This ensures each test starts with a clean slate, preventing any cross-test interference.
When you run this test, you should see output similar to:
Jest offers four main setups and teardown hooks, each with specific use cases:
beforeEach: Runs before each test, ideal for resetting state or creating fresh test dataafterEach: Runs after each test, perfect for cleanup operationsbeforeAll: Runs once before all tests in a describe block, good for expensive setup operationsafterAll: Runs once after all tests in a describe block, useful for final cleanup
For example, when working with database connections or other expensive resources, you might prefer beforeAll and afterAll to avoid repeated setup costs:
These setups and teardown hooks will create more organized tests that properly isolate each test case while avoiding repetitive code.
This approach leads to a more maintainable test suite and helps ensure your tests accurately represent how your code should behave in real-world scenarios.
Step 7 — Code coverage with Jest
Code coverage helps you understand how thoroughly your code is being tested. Jest includes built-in coverage reporting capabilities that make identifying untested areas of your code and improving your testing strategy easy.
To enable coverage reporting in Jest, you only need to add the --coverage flag when running your tests.
Update the package.json file to include a dedicated script for running tests with coverage:
Now, you can generate a coverage report by running:
This will execute your tests and produce a detailed coverage report. The output will look similar to this:
Running your tests will generate a detailed coverage report that provides insights into how well your code is tested. The report includes several key metrics:
- Statement coverage – Measures the percentage of executed statements.
- Branch coverage – Tracks how many control structures, such as
ifstatements, were tested. - Function coverage – Indicates the percentage of functions that were called.
- Line coverage – Reflects the proportion of executed executable lines.
In this report, math.js achieves 100% coverage across all metrics. This means every statement, branch, function, and line in the file is tested. However, fileReader.js has only 75% line coverage, indicating that some code paths remain untested. Specifically, the tests do not cover lines 8-9, which handle error scenarios. Addressing these gaps can help improve overall test coverage and ensure robust error handling in your code.
Let's improve the coverage by adding a test for the error case in fileReader.js:
The new changes modify the beforeEach hook by removing the default mock implementation of readFile.mockResolvedValue("Mocked file content"), ensuring that each test explicitly defines its own mock behavior.
The existing test is also updated to explicitly configure the mock for a successful file read, making each test more independent and preventing unintended mock behaviors from carrying over.
Additionally, a new test case is added to verify that the function correctly throws an error when file reading fails.
Running the coverage report again reflects these improvements, with all metrics reaching 100%:
The percentage values in the report are highlighted in green, indicating that every statement, branch, function, and line of code is fully tested.
Jest's coverage reporting also generates a detailed HTML report you can view in your browser. By default, this report is saved in the coverage directory at the root of your project:
Open coverage/lcov-report/index.html in your browser to see a more interactive version of the coverage report.
With coverage reports in place, you can adopt a coverage-driven testing approach to help ensure your codebase is thoroughly tested and more resilient to changes.
Final thoughts
In this guide, you’ve explored Jest's core features and learned how to write, run, and optimize tests effectively.
You started by setting up a Jest testing environment, writing and running your first test, and then explored more advanced topics like test filtering, mocking, setup and teardown hooks, and code coverage.
To further enhance your testing skills, dive into Jest’s official documentation, where you'll find more in-depth information to take your testing workflow to the next level.
Happy testing!