Jest provides a testing framework for JavaScript, complete with a built-in coverage tool that generates reports on how much code is covered by tests. The framework supports various types of tests including unit, integration, and end-to-end, streamlining the testing process across different layers of your application.
- Configuration/Tests:
jest.config.js
- Main configuration file that controls Jest testingjest_setup.ts
- Ran after environment setup, but before tests are ran
- Adds custom Jest matchers that help with assertions for DOM nodes
- Manual configuration to mock modules with named default exports due to inconsistencies (see below "Named Default Module Issues")
- Included in
tsconfig.json
includes so matcher includes are automatically included
src/lib/*.test.ts
- Unit tests for project libraries/classessrc/components/*.test.ts
- Integration tests for React componentssrc/partials/*.test.ts
- Integration tests for application partials (also React components)
package.json
scripts:npm run tests
- Runs the tests with--silent
to suppress console.log/error statementsnpm run tests_verbose
- Runs the tests with full console outputnpm run coverage
- Runs the*.test.ts
tests in./src/lib
with--silent
and--coverage
options to suppress console.log/error statements and generate code coverage reportsnpm run coverage_verbose
- Runs the*.test.ts
tests in./src/lib
with--coverage
option to generate code coverage reports with full console output
package.json --save-dev
Development Dependencies:jest
- JavaScript Testing Framework with a focus on simplicity@types/jest
- Type definitions for Jestts-node
- TypeScript execution and REPL for Node.js, Jest requires it to read TypeScript configuration filests-jest
- Jest transformer with source map support that lets you use Jest to test projects written in TypeScripteslint-plugin-jest
- ESLint plugin for Jest@testing-library/jest-dom
- Custom jest matchers to test the state of the DOM@testing-library/react
- React DOM testing utilitiesjest-axe
- Custom Jest matcher for axe for testing accessibility@types/jest-axe
- type definitions forjest-axe
react-test-renderer
- Experimental React renderer that can be used to render React components to pure JavaScript objects, without depending on the DOM or a native mobile environment@types/react-test-renderer
- type definitions forreact-test-renderer
identity-obj-proxy
- An identity object using ES6 proxies like CSS modulesjest-environment-jsdom
- Jest environment for "jsdom" configuration
Modern front-end testing requires multiple types of tests to be ran in different contexts to fully validate an application. Below covers all of the types of testing handled by Jest.
- Ran with
npm run tests
andnpm run tests_verbose
- Located in
src/lib/*.test.ts
- Tests libraries that do not have front-end concerns; ex: a HTTP client wrapper
- Ran with
npm run tests
andnpm run tests_verbose
- Located in
src/components/*.test.ts
andsrc/partials/*.test.ts
- Uses
react-testing-library
utility functions on top ofreact-dom
andreact-dom/test-utils
that allow you to emulate the DOM with normal interactions - Used to test React components
- Ran with
npm run coverage
andnpm run coverage_verbose
- Consists of both above "Unit Tests" and "Integration Tests" above
- Ensures all code is reachable
- Coverage reporting generates in
./coverage
- Ran with
npm run tests
andnpm run tests_verbose
- Included in the main
./src/App.tsx
testing and takes fully rendered snapshot of application - Generates snapshots in
./src/__snapshots__
directory which are checked-in via SCM - Update the snapshots via
npm run tests -- -u src/App.test.tsx
and check-in results
- Ran with
npm run tests
andnpm run tests_verbose
- Included in "Integration Tests" above
- Uses
jest-axe
to check for common accessibility issues; ex: missing alt tags on images
Not handled by Jest - see playwright.md docs for more information as these need to be ran separate from Jest tests.
The --experimental-vm-modules
flag is required when running jest.js
with Node.js binary. This will generate a warning about using an experimental flag which can be ignored with --no-warnings
. The following is what the base npm run test
package.json
script command:
# Run all Jest unit and integration tests
node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js
More on Jest ECMAScript Modules »
NOTE: Both of these are optional!
--detectOpenHandles
- Attempts to report on open handles preventing Jest from exiting cleanly; ex: a non-clearedsetTimeout
--silent
- Hideconsole.*
output to console and only show test/coverage results; this flag is the difference between thenpm run tests
and thenpm run tests_verbose
package.json
scripts
# Run all Jest unit and integration tests with recommended Jest options
node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js --detectOpenHandles --silent
node --no-warnings --experimental-vm-modules node_modules/jest/bin/jest.js --detectOpenHandles
More on --detectOpenHandles
»
When front-end code includes CSS or static asset modules it will cause syntax errors with Jest. To avoid this we use moduleNameMapper
in jest.config.js
to map these assets to safe mocked versions.
- CSS modules use
identity-obj-proxy
- Static asset modules use
file_mock.ts
to return a simple string representing the asset - More information on both CSS/static module mocking - https://jestjs.io/docs/webpack#mocking-css-modules
A named default export looks like the following:
// Real-world example
import json from "highlight.js/lib/languages/json";
export default json;
These are handled different between esbuild and Jest contexts; shown below is the difference of the scope, showing Jest loaded the module as CJS nesting the expected scope under jest.default
whereas esbuild handles it as-expected:
import json from 'react-syntax-highlighter/dist/esm/languages/hljs/json';
// esbuild
json
// Jest
json.default
Short version: Jest and Node's module resolvers are behaving different causing named module imports to be processed as CJS, falling through to cjs-module-lexer
.
Full version:
- Why this issue is happening in Jest - jestjs/jest#11563 (comment)
cjs-module-lexer
author input (referenced in above issue comment) - nodejs/cjs-module-lexer#57 (comment)
To fix this we mock the module manually before tests run in jest_setup.ts, removing the .default
if needed. To do this, add the offending module import to JestSetup.fix_exported_modules_w_named_defaults
and the mock will be setup for you. This ensures that imports work as-expected in the esbuild and Jest contexts, avoiding ugly json.default || json
logic.
Example:
Assuming the import used in our example above, add the import string to jest_setup.ts:
this.fix_exported_modules_w_named_defaults = [
'react-syntax-highlighter/dist/esm/languages/hljs/json'
];
By default no node_modules
modules are included in the ts-jest
transform outlined in the jest.config.js
transform
property. Sometimes we may need these to be transformed when testing. There's a utility array that will handle this at the top of jest.config.js
- transform_node_modules
. Just add the node_modules
packages to this and they will be transformed with the same ts-jest
transformer that the rest of the application uses.
const transform_node_modules = [
'react-syntax-highlighter'
];
We use TypeScript path aliases so that means we need to add the same support to Jest using the moduleNameMapper
property. To add a path alias copy the existing @src/
one:
moduleNameMapper: {
// ...
// Map the @src path alias defined in `tsconfig.json`
'^@src/(.*)' : '<rootDir>/src/$1',
// ...
},