Jest is one of the most popular JavaScript testing libraries used for both unit and integration testing. It supports several JavaScript frameworks, including React, Angular, NestJS, and Express, making it a suitable testing option for both frontend and backend JavaScript projects.
In this article, we will dive deep into all things Jest to help you determine whether Jest is the right choice for your project. We’ll cover what is it, how it works, its features and limitations, and how it compares to other JavaScript testing libraries.
The Jest testing framework was developed by Facebook (now Meta) with the goal of testing React components. It’s designed to assert the correctness of JavaScript code by utilizing a feature-rich and well-documented API.
Jest currently comes with support for Babel, TypeScript, Node, React, Angular, Vue, and several other JavaScript frameworks. It provides functionalities like mocking, assertion, code coverage, and exception context which help developers write tests on the behavior and outcome of the code.
Additionally, Jest provides support for mocking and spying on external dependencies, which makes it easy to test complex code.
Jest was originally referred to as “jst” internally at Meta. The Meta team created Jest in 2011 when they rewrote their chat feature in JavaScript. It was open sourced in 2014 and maintained part-time by Meta engineers until 2016, when a dedicated team was formed to maintain it.
Jest’s community of open source contributors grew. In 2018, Meta founded the Jest Open Collective and raised over $70,000 in funds to support non-Meta employees contributing to, developing, and maintaining Jest.
Today, Jest is primarily maintained by a group of core, non-Meta-employee contributors to whom Meta has transferred ownership via the OpenJS Foundation.
Jest is built of multiple, individually usable packages. These packages play different parts in executing a test with Jest via a command such as jest my-test.js
. Let’s take a look at how this happens.
When you run the jest my-test.js
command, the first package called is jest-cli
. This package executes the test, calling all other needed packages as necessary. When the test is initiated, jest my-test.js
calls the jest-config
package.
The Jest config takes the arguments in the test execution command and combines them with the config, typically defined in either the package.json
file or the jest.config.json
file. It then creates two config objects — the Global
config and the Project
config — and sends those back to the Jest CLI.
Jest then performs some static file analysis using the jest-haste-map
package. This package helps Jest find all relevant files in the project, understand dependencies between files, and know what files might be affected by a given code change.
This static file analysis step produces a HasteContext
along with a HasteFS
or the Haste File System, which is a list of all the files and their dependencies in the project. These are cached so that they only need to be recreated when there are file changes. Then, they’re reported back to the Jest CLI.
Now that all the preparatory work is done, Jest has a context
object defined. This object includes the HasteFS
, the Global
config, and the Project
config.
Let’s go back to the command that started it all:
jest my-test.js
Jest treats my-test.js
as a pattern, not as a path. So, the Jest CLI — utilizing its internal search function and the HasteFS
— finds all test files to run that match the my-test.js
pattern.
The Jest CLI then runs an additional step to order the tests, prioritizing tests that failed in the past to provide insights into a fault as early as possible. Next, it calls on the jest-runner
package to schedule the tests using the priority order generated in the previous step.
Then, the tests are executed according to the test schedule using the jest-circus
package. This package also provides some of Jest’s popular functions, like describe
and it
.
Each test is run in isolation using the jest-runtime
module, which runs each test in an isolated virtual machine (VM) context, and the jest-environment
module. At this point, mocking and the needed transformations are done for each test dependency resolution.
The results of the tests run are updated in the TestResults
object by jest-circus
. Then, they’re passed on to the reporters for display and aggregation.
Here’s a simplified overview of the Jest testing architecture and execution.
You can find a more detailed and complex view of Jest’s architecture and executions in this LinkedIn article.
In the upcoming sections, we’ll take a look at some of the standout features that make Jest a great tool.
The Jest command line tool provides the jest
command for initiating tests, along with a variety of options you can enable when using Jest to run tests.
One of Jest’s biggest perks is how much customization it provides through its configuration — i.e., in the package.json
or jest.config.json
files. What’s even better is that all configuration options are available on the CLI tool.
Some of the most popular Jest CLI options are:
watch
: Tells Jest CLI to watch for file changes and re-run related tests to the edited filessilent
: Prevents the tests from printing output messages to the command line while runningverbose
: Enables detailed output on test runs and displays the test suite hierarchyfindRelatedTests
: Takes a space separated list of source files as an argument and is used to run tests related the listed files. This helps the developer run the minimum number of tests needed to validate specific changes. It’s also commonly used along with the coverage
option to provide the test coverage of the listed filesJest provides a code coverage configuration, which helps highlight code paths that are not covered by tests. You can enable this feature by passing the --coverage
CLI option when running the Jest test command.
The Jest testing platform is able to adapt to any JavaScript library or framework. Jest supports many popular JavaScript frameworks and libraries with active communities. These include React, Vue.js, Angular, MobX, Redux, Express.js, GatsbyJS, Hapi.js, and Next.js.
Jest provides an option to sandbox and isolate modules which may have been mocked or spied in the execution of a test. This option is written as jest.isolateModules(fn)
and it takes a callback returning the modules to isolate. Here’s an example:
let myModule; jest.isolateModules(() => { myModule = require('myModule'); }); const otherCopyOfMyModule = require('myModule');
Jest provides a mock function that allows us to replace the actual implementation of a function. Instead of implementing the function, we can use this mock function to keep track of calls and parameters passed into the calls, along with setting return values for the calls.
You can create a mock function can be created with the jest.fn()
command. Meanwhile, you can mock a module using the jest.mock(<module>)
command.
Snapshot testing is an option of Jest used to validate that some UI component is rendered as expected. The test renders a UI component, takes a snapshot and compares to a reference snapshot saved alongside the test. Snapshot assertions are executed using the format expect(rendered-component).toMatchSnapshot()
.
When executed the first time, Jest creates a snapshot file referenced for asserting future tests. This helps ensure that while making changes to an application, an unexpected change to a component does occur. Jest also provides an option to intentionally update the snapshot reference using the option --updateSnapshot
.
In addition to snapshot testing, Jest enables you to perform unit tests, integration tests, component tests, and visual regression tests.
The Jest Assertions API provides a variety of options to check that specific conditions are met. You can access these assertion options via the expect
API function.
Matchers, on the other hand, are assertion functions that compare a given value to some other expected value or condition. Some examples of popular matcher functions are:
toBe(value)
: Compares the value of some variable to what we expect its value to be. You could use it like so: expect(receivedVar).toBe(expectedValue)
toHaveBeenCalled()
: Used on a mock function to assert that the function was indeed invokedtoHaveBeenCalledTimes(number)
: Also used on a mock function to assert the number of times the function was invokedtoEqual(value)
: Used to assert that some variable is exactly equal to some expected valuetoThrow(error?)
: Used to assert that an exception was thrown in the execution of a function. You can also optionally pass an argument of either the error class, an instance of the error class, or a substring of the error messageNote that when we assert that a function execution throws an error, our function call must be wrapped in another function, like so:
expect(() => { … function call }).toThrow()
Jest provides many other matcher functions, which you can explore in more detail in the docs.
Jest provides hooks as a mechanism to attach some function — for example, a callback to run some things — during certain events in the execution of a test suite.
The most common hook events are the beforeAll
and afterAll
hooks, which are popularly referred to as setup and teardown. These setup and teardown hooks are used to execute functions like seeding, setting mock responses, or clearing and resetting mocks while executing tests.
There are many other hooks available besides beforeAll
and afterAll
. For example, beforeEach
and afterEach
execute code in its callback before each test.
Jest provides blocks as a way to group and improve the context of related test cases in a file. A block can contain both hooks and tests in an organized and contextual way.
Jest defines these blocks using the function describe
, which takes the following arguments: a string describing the block and a callback function executing the block body. Here’s an example:
// Block example describe('block description', () => { // hook within the block beforeEach(() => { // run some setup tasks }) test('test case 1', () => { // test description }) })
In Jest, scoping refers to the order of relevance and execution of the hooks and functions within a test file. Scoping defines which hooks are relevant to which test blocks and the order in which they are executed.
For example, the top-level beforeEach
and afterEach
hooks — i.e., those outside all blocks — are executed for every test in the file. Meanwhile, a beforeEach
hook within a describe
block is only run before each test within that block.
Jest provides an interactive watch mode. You can enable this mode by adding the --watch
argument when running the Jest command.
With watch mode, Jest looks out for edits to the code files and reacts to these changes by re-running tests related to them.
.only
and .skip
annotationsJest provides an option to run a single test or block by using the .only
annotation, like so:
// Running a single test test.only(fn) // Running a single block of tests describe.only(() => { test(fn) ... })
Also, as an alternative to running a single test or block of tests, Jest provides an option to skip certain tests or blocks using the .skip
annotation:
// Skipping a single test test.skip(fn) // Skipping a single block of tests describe.skip(() => { test(fn) ... })
Now that we’ve explored Jest and some of its key features, let’s review the top reasons why you should use Jest:
Jest has many excellent features, comprehensive testing capabilities, and ease of use — along with wide support for many JavaScript frameworks and libraries. These make it a great choice for most JavaScript projects and many TypeScript projects as well.
As we discuss when, how, and why to use Jest, it’s also important to consider its limitations. Here are a few you should keep in mind:
Despite these limitations, Jest is still an excellent testing framework that is simple to use and even simpler to set up. It’s designed to work without custom configuration, and it provides a wide range of testing API functionality suitable for testing both backend and frontend JavaScript projects.
Jest is a great unit and integration testing library option. Here are some specific testing use cases where Jest really shines:
Beyond the ideas listed above, there are many other use cases for which Jest is a great choice. I encourage you to review its features and benefits to determine whether it works for your particular case.
There are several other JavaScript testing libraries. In this section we will compare a few of them with Jest.
Mocha, unlike Jest, doesn’t have inbuilt assertion or mocking capabilities. Instead, it relies on third-party tools like Sinon and Chai to provide these functionalities.
This reliance on plugins and external libraries makes Mocha flexible and extensible to choose your preferred assertion and mocking libraries. However, it also increases the complexity to get started and maintain its use in a project.
Mocha also provides additional functionalities, like:
Like with Jest, you can configure Mocha to work with multiple JavaScript frameworks — like React, Angular, Express, and more — covering both frontend and backend testing needs. Mocha also has full support for ES modules, while Jest’s support is limited and experimental.
Jasmine is an open source JavaScript testing framework. Similar to Jest, Jasmine has built-in assertion functionality and doesn’t require configuring external functionality for this.
Unlike Jest, everything needed to run Jasmine comes in a single package. Jasmine was developed in 2010 before Mocha and Jest, making it one of the oldest JavaScript testing frameworks.
Jasmine also supports most of the popular JavaScript frameworks, has highly readable tests, and has very good tool and community support.
The Node.js test runner module, node:test
, was released experimentally in version 18 and made stable in version 20. It provides some limited testing functionality to avoid Node developers being forced to rely on a third part library.
The Node test module provides a single function, within which single tests as call back functions or multiple subtests can be created. An example;
// node-test.test.js const test = require('node:test') // Single test test('should pass', (t) => { assert.strictEqual(1, 1); }); // Multiple sub tests test('has multiple tests', (t) => { t.test('subtest 1', (t) => { assert.strictEqual(1, 1); }); t.test('subtest 2', (t) => { assert.strictEqual(2, 2); }); })
The Node.js test module similarly to Jest provides assertions, mocking, the skip/only
options to run or skip specific tests, watch mode, code coverage, test hooks and the describe/it
syntax for writing and grouping tests.
We’ve covered a lot of information in this section. Let’s summarize the information in the table below:
Features | Jest | Mocha | Jasmine | Node.js test module |
---|---|---|---|---|
Assertions | Built-in | External library i.e. Chai | Built-in | Built-in |
Mocks — stubs & spies | Built-in | External library i.e. Sinon | Built-in | Built-in |
Code coverage & reporting | Built-in | Built-in | Built-in | Limited |
Test hooks | Included | Included | Included | Included |
Asynchronous testing | Included | Included | Included | |
Parallel testing | Built-in | Not available | Available in v5.0.0 and later | Configurable |
Browser test | Not available | Not available | Available | Not available |
Snapshot testing | Available | Not available | Configurable via a third party library | Not available |
You can use this table to help you quickly assess each library’s features and determine whether Jest or another option might be best for your needs.
Earlier, we compared Jest with several other test libraries. However, it’s useful to mention that we can also integrate Jest with other libraries to achieve more extensive testing.
A popular example is using Jest with Enzyme to test React UI components. Jest and Enzyme serve different purposes. Jest is a full testing framework providing test blocks, assertions, and several other features. Enzyme is a library that makes it easier to assert the correctness of React components by providing rendering functions.
When using Jest and Enzyme together, we can use Enzyme’s render function to render the component and then use Jest’s assertion function to validate some content. Here’s an example:
// Jest & Enzyme example import MyParentComponent from './MyParentComponent'; import MyChildComponent from './MyChildComponent'; describe('<MyComponent />', () => { it('renders three <MyChildComponent /> components', () => { const wrapper = shallow(<MyParentComponent />); expect(wrapper.find(MyChildComponent)).to.have.lengthOf(3); }); });
Now that we’re familiar with Jest and its capabilities and limitations, let’s look at how to add Jest to an existing project.
First, install Jest using your preferred package manager. In this article, I’ll be using Yarn:
$ yarn add --dev jest
Next, add the following to your package.json
file:
// package.json { "scripts": { "test": "jest" } }
With these two simple steps, you’re ready to write some Jest tests in JavaScript code! However, Jest provides the option to make some more advanced configurations. We can create the Jest configuration file by running the command below:
$ yarn create jest@latest
You can find more Jest configuration options in the docs for Babel, webpack, and TypeScript.
Jest is feature-rich testing framework that comes with several in-built features like assertion, mocking, parallel test running, and more. It’s powerful, easy to use, and one of my favorite test frameworks.
Getting started with Jest is easy thanks to its useful defaults, but you can also configure it with more custom options. Jest also has a rich API, is well documented, and has a large and active community of contributors. This means there’s always lots of content available on its features and best practices.
I hope this guide was useful in helping you evaluate Jest as a testing tool for your next project. If you have any further questions about Jest, feel free to comment below.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.