/ #jest #testing #javascript 

What is Jest and why should I use it?

Jest is a delightful JavaScript Testing Framework with a focus on simplicity.

jestjs.io

Jest is a batteries-included framework. Due to its fully-featured nature and large surface area, we can compare it to test runners (Mocha), assertion libraries (Chai, ShouldJS, power-assert, expect.js), integrated test-runner + assertion libraries (QUnit, Protractor, Tape, AVA, Jasmine), stubbing libraries (Sinon), module interception libraries (Proxyquire), coverage tools (NYC, Istanbul).

Table of Contents

Under the hood, Jest actually uses some of these other libraries, namely Jasmine and Istanbul/NYC. In many ways it can be thought of as an modern JavaScript integrated testing environment. As per the documentation, it can be used to test all types of JavaScript projects, with supported setups ranging from bare JavaScript setups to systems build on top of Babel, TypeScript, Node, React, Vue or Angular.

Its selling points (as per jestjs.io) are:

  • zero config: it should take little to no configuration to get Jest working in your JavaScript project
  • snapshots: a simple system to keep track of changes within a system
  • isolated: tests are isolated from each other, this means fewer issues with tests modifying shared resources like global objects or module mocks.
  • familiar/“great” API: Jest supports helper methods to write your tests with, including describe, it, test. Tests are Promise-aware (and therefore support async functions as tests), for example if a Promise returned from a test rejects or an async test throws, the test will fail with the corresponding error.

Jest is still the leading test framework in terms of downloads as of January as per npmtrends Jest vs Mocha vs AVA vs Jasmine vs QUnit vs Chai. Jest came out of efforts from Facebook. It’s is used at a plethora of small and large companies including Facebook, AirBnB, The New York Times and Spotify.

All time trends (January 2024) for Jest vs Mocha, AVA, Jasmine, QUnit and Chai

Caveats in 2023 and beyond

The bulk of this post come from the introduction of the Jest Handbook, released in 2020 and written throughout 2018-2020.

The JavaScript ecosystem has shifted since, in fact in the intervening years, Jest has become a de-facto default for testing in JavaScript. For frontends, backends and beyond, regardless of frameworks.

One big issue that is now facing Jest is ECMAScript Modules (ESM). Jest was built around the Node.js CommonJS (require()) module system and has found it difficult to also support ESM in Node, see this GitHub Issue tracking work to support ESM in Jest.

The team building Jest went from being employed in Facebook open source to a few maintainers outside of that organisation and the project itself a part of the OpenJS foundation, see Meta Open Source is transferring Jest to the OpenJS Foundation and the related GitHub Issue.

The new alternative to Jest in terms of fully-featured, batteries-included is Vitest. Which has the benefit of being built on top of the Vite bundler and does not have Jest’s issue around ESM. See Vitest comparison with Jest on the Vitest docs site.

Finally, following on from the success that Go has had with its built-in test runner, new JavaScript runtimes like Deno (1.0 in 2020) or Bun (1.0 in 2023) were built with an included test runner.

In fact, Node.js got its built-in test runner in 2022, see my post about the Node.js test runner.

You can set up code transforms, get coverage, set coverage thresholds, have an interactive CLI, run/skip single tests, mock/intercept modules, mock timers, create mock/stub/spies.

Feature table for reference:

NameCoverage Collection *Module/import InterceptionTimer mockingStub/Spy functionalityAssertionsInteractive Watch
Mocha
AVA
Jasmine
QUnit
ChaiN/AN/AN/A
ShouldJSN/AN/AN/A
power-assertN/AN/AN/A
expect.jsN/AN/AN/A
SinonN/AN/AN/AN/A
ProxyquireN/AN/AN/AN/A
NYC/Istanbul **N/AN/AN/AN/A
Jest

* Mocha, AVA, Jasmine and QUnit can integrate with code coverage tools nyc/istanbul but don’t come with coverage out of the box.

** nyc is the command line interface for Istanbul, “a JavaScript test coverage tool”

An interactive CLI

Jest’s command line interface (CLI) has sane defaults and robust configuration options.

The interactive watch mode can be used to debug specific tests or files. You can enable using the --watch option, for example using the jest --watch command. By default the watch mode watches only files that have changed since the last commit. To watch all files (without the git commit filtering), you can use --watchAll.

The interactive watch mode allows you to filter by failing tests with the “f” filter, file name (regex) match with the “p” filter, test name (regex) match with the “t”. Other filter options include “a” to run all tests, “c” to clear the current filter and “w” to show all filters.

Isolated tests with parallel execution

Jest runs each test suite (each file that Jest matches as a “test file) in an isolated JavaScript process.

Running each test file in a new context means that there doesn’t tend to be contention on global state. For example, a scenario where the might be contention is if two tests mock the behaviour of a module or a JavaScript global in different ways.

With isolation also comes the opportunity to parallelise test execution. Parallel execution accentuates and highlights race conditions or contentious reliance on shared resources. Due to the isolation between test files, Jest can run tests in parallel.

This means less hacks to work around race conditions in your tests.

Built-in coverage

In order to enable coverage reports for your tests in Jest, one has to pass the --coverage flag to the Jest CLI.

For example if your usual test command is jest src (runs all tests matched in src directory), you would enable coverage by using jest src --coverage.

Once the run completes you will get the coverage report printed to the CLI screen as well as put in a couple of different formats in a coverage folder.

Of the coverage output formats, there’s a human-readable HTML version. If you open coverage/lcov-report/index.html in a browser, you can inspect which files, functions, statements and branches are coverage by your tests.

Another nice touch of the Jest CLI is that if you run the client in watch mode with coverage using jest --watch --coverage, it will output the coverage on every “watch” test re-run.

Better tests with specific assertions & matchers

Jest’s expect().toX assertions will be familiar to Jasmine users. Jest exposes all the core Jasmine assertions including toEqual, toBe, toBeInstanceOf, toBeLessThan(OrEqual), toBeGreaterThan(OrEqual), toContain, toMatch, toThrow.

Jest also includes a couple of extra array/Object matchers in toHaveLength, toHaveProperty. Since Jasmine doesn’t have built-in snapshot assertions, those are a Jest addition with toMatchSnapshot, toMatchInlineSnapshot.

Jest also includes more advanced partial and non-literal matching features with the partial matchers expect.arrayContaining(['some', 'array', 'values']), expect.objectContaining({some: 'object', entries: 'here'}) and non-literal matchers expect.anything, expect.any(constructor).

Promise and async/await-aware tests

Jest exposes a battery of Promise-aware matchers that can be chained onto, .resolves and .rejects in order to assert on a Promise’s outcome.

A Jest test can return a Promise or be an async function. If the test is a Promise, Jest will handle it accordingly, failing on rejections and succeeding if the assertions pass and there are no rejections.

Necessary extension points for modern JavaScript setups

Webpack + other bundles support “importing” non-JS files for post-processing of assets.

Examples of this are image/asset imports, .vue single-file components in Vue codebases.

Jest gives extensions points for both of these use cases.

You can use the moduleNameMapper to point to (mock) JavaScript files in order to stub imports that match a regular expression.

Jest also supports custom transforms. This allows .vue and other compile-to-JS files to be tested using Jest.

Easy to migrate to

It gives you migrations codemods from other frameworks.

At its core, the assertions used are Jasmine-style, with an extended assertion set, for example toHaveBeenCalled which integrates tightly with Jest stubs.

Conclusion

Jest brings all the best features of JavaScript’s top testing tools together in a low-config and comprehensive package.

If you’re interested in advanced Jest features or keen to improve your JavaScript testing fundamentals, The Jest Handbook is available now and has helped hundreds of developers hone their skills with Jest, a mainstay of JavaScript testing.

Author

Hugo Di Francesco

Co-author of "Professional JavaScript", "Front-End Development Projects with Vue.js" with Packt, "The Jest Handbook" (self-published). Hugo runs the Code with Hugo website helping over 100,000 developers every month and holds an MEng in Mathematical Computation from University College London (UCL). He has used JavaScript extensively to create scalable and performant platforms at companies such as Canon, Elsevier and (currently) Eurostar.

Curious about Advanced Jest Testing Features?

Take your JavaScript testing to the next level by learning the ins and outs of Jest, the top JavaScript testing library. Get "The Jest Handbook" (100 pages)