nx/docs/shared/packages/jest/jest-plugin.md
2024-04-09 16:02:05 +02:00

283 lines
9.7 KiB
Markdown

---
title: Overview of the Nx Jest Plugin
description: The Nx Plugin for Jest contains executors and generators that support testing projects using Jest. This page also explains how to configure Jest on your Nx workspace.
---
[Jest](https://jestjs.io/) is an open source test runner created by Facebook. It has a lot of great features:
- Immersive watch mode for providing near instant feedback when developing tests.
- Snapshot testing for validating features.
- Great built-in reporter for printing out test results.
## Setting Up @nx/jest
### Installation
{% callout type="note" title="Keep Nx Package Versions In Sync" %}
Make sure to install the `@nx/jest` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/recipes/tips-n-tricks/keep-nx-versions-in-sync).
{% /callout %}
In any Nx workspace, you can install `@nx/jest` by running the following command:
{% tabs %}
{% tab label="Nx 18+" %}
```shell {% skipRescope=true %}
nx add @nx/jest
```
This will install the correct version of `@nx/jest`.
{% /tab %}
{% tab label="Nx < 18" %}
Install the `@nx/jest` package with your package manager.
```shell
npm add -D @nx/jest
```
{% /tab %}
{% /tabs %}
### How @nx/jest Infers Tasks
{% callout type="note" title="Inferred Tasks" %}
Since Nx 18, Nx plugins can infer tasks for your projects based on the configuration of different tools. You can read more about it at the [Inferred Tasks concept page](/concepts/inferred-tasks).
{% /callout %}
The `@nx/jest` plugin will create a task for any project that has an Jest configuration file present. Any of the following files will be recognized as an Jest configuration file:
- `jest.config.js`
- `jest.config.ts`
- `jest.config.mjs`
- `jest.config.mts`
- `jest.config.cjs`
- `jest.config.cts`
### View Inferred Tasks
To view inferred tasks for a project, open the [project details view](/concepts/inferred-tasks) in Nx Console or run `nx show project my-project --web` in the command line.
### @nx/jest Configuration
The `@nx/jest/plugin` is configured in the `plugins` array in `nx.json`.
```json {% fileName="nx.json" %}
{
"plugins": [
{
"plugin": "@nx/jest/plugin",
"options": {
"targetName": "test"
}
}
]
}
```
- The `targetName` option controls the name of the inferred Jest tasks. The default name is `test`.
## Using Jest
### Generate a new project set up with Jest
By default, Nx will use Jest when creating applications and libraries.
```shell
nx g @nx/web:app frontend
```
### Add Jest to a project
Run the `configuration` generator
```shell
nx g @nx/jest:configuration --project=<project-name>
```
> Hint: You can use the `--dry-run` flag to see what will be generated.
Replacing `<project-name>` with the name of the project you're wanting to add Jest too.
### Testing Applications
The recommended way to run/debug Jest tests via an editor
- [VSCode](https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner)
- [Webstorm](https://blog.jetbrains.com/webstorm/2018/10/testing-with-jest-in-webstorm/)
To run Jest tests via nx use
```shell
nx test frontend
```
### Watching for Changes
Using the `--watch` flag will run the tests whenever a file changes.
```shell
nx test frontend --watch
```
### Snapshot Testing
Jest has support for **Snapshot Testing**, a tool which simplifies validating data. Check out the [official Jest Documentation on Snapshot Testing](https://jestjs.io/docs/en/snapshot-testing).
Example of using snapshots:
```typescript
describe('SuperAwesomeFunction', () => {
it('should return the correct data shape', () => {
const actual = superAwesomeFunction();
expect(actual).toMatchSnapshot();
});
});
```
When using snapshots, you can update them with the `--updateSnapshot` flag, `-u` for short.
> By default, snapshots will be generated when there are not existing snapshots for the associated test.
```shell
nx test frontend -u
```
Snapshot files should be checked in with your code.
### Performance in CI
Typically, in CI it's recommended to use `nx affected -t test --parallel=[# CPUs] -- --runInBand` for the best performance.
This is because each [jest process creates a workers based on system resources](https://jestjs.io/docs/cli#--maxworkersnumstring), running multiple projects via nx and using jest workers will create too many process overall causing the system to run slower than desired. Using the `--runInBand` flag tells jest to run in a single process.
## Configurations
### Jest
Primary configurations for Jest will be via the `jest.config.ts` file that generated for your project. This file will extend the root `jest.preset.js` file. Learn more about [Jest configurations](https://jestjs.io/docs/configuration#options).
The root level `jest.config.ts` file configures [Jest multi project support](https://jestjs.io/docs/configuration#projects-arraystring--projectconfig).
This configuration allows editor/IDE integrations to pick up individual project's configurations rather than the one at the root.
{% tabs %}
{% tab label="Nx 18+" %}
The set of Jest projects within Nx workspaces tends to change. Instead of statically defining a list in `jest.config.ts`, Nx provides a utility function called `getJestProjectsAsync` which retrieves a list of paths to all the Jest config files from projects using the `@nx/jest:jest` executor or with tasks running the `jest` command.
You can manually add Jest projects not identified by the `getJestProjectsAsync` function by doing something like the following:
```typescript {% fileName="jest.config.ts" %}
import { getJestProjectsAsync } from '@nx/jest';
export default async () => ({
projects: [
...(await getJestProjectsAsync()),
'<rootDir>/path/to/jest.config.ts',
],
});
```
{% /tab %}
{% tab label="Nx < 18" %}
The set of Jest projects within Nx workspaces tends to change. Instead of statically defining a list in `jest.config.ts`, Nx provides a utility function called `getJestProjects` which queries for Jest configurations defined for targets which use the `@nx/jest:jest` executor.
You can add Jest projects which are not included in `getJestProjects()`, because they do not use the Nx Jest executor, by doing something like the following:
```typescript {% fileName="jest.config.ts" %}
import { getJestProjects } from '@nx/jest';
export default {
projects: [...getJestProjects(), '<rootDir>/path/to/jest.config.ts'],
};
```
{% /tab %}
{% /tabs %}
### Nx
The Nx task options can be configured via the [project config file](/reference/project-configuration) or via the command line flags.
If you're using [inferred tasks](/concepts/inferred-tasks), or running Jest directly with the `nx:run-commands` executor, you can [provide the Jest args](/recipes/running-tasks/pass-args-to-commands) for the command you're running.
If you're using the `@nx/jest:jest` executor, you can provide [the options the executor accepts](/nx-api/jest/executors/jest#options).
### Code Coverage
Enable code coverage with the `--coverage` flag or by adding it to the executor options in the [project configuration file](/reference/project-configuration).
By default, coverage reports will be generated in the `coverage/` directory under projects name. i.e. `coverage/apps/frontend`. Modify this directory with the `--coverageDirectory` flag. Coverage reporters can also be customized with the `--coverageReporters` flag.
> `coverageDirectory` and `coverageReporters` are configurable via the project configuration file as well.
### Global setup/teardown with nx libraries
In order to use Jest's global setup/teardown functions that reference nx libraries, you'll need to register the TS path for jest to resolve the libraries.
Nx provides a helper function that you can import within your setup/teardown file.
```typescript {% fileName="global-setup.ts" %}
import { registerTsProject } from '@nx/js/src/internal';
const cleanupRegisteredPaths = registerTsProject('./tsconfig.base.json');
import { yourFancyFunction } from '@some-org/my-util-library';
export default async function () {
yourFancyFunction();
}
// make sure to run the clean up!
cleanupRegisteredPaths();
```
If you're using `@swc/jest` and a global setup/teardown file, you have to set the `noInterop: false` and use dynamic imports within the setup function:
```typescript {% fileName="apps/<your-project>/jest.config.ts" %}
/* eslint-disable */
import { readFileSync } from 'fs';
// Reading the SWC compilation config and remove the "exclude"
// for the test files to be compiled by SWC
const { exclude: _, ...swcJestConfig } = JSON.parse(
readFileSync(`${__dirname}/.swcrc`, 'utf-8')
);
// disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves.
// If we do not disable this, SWC Core will read .swcrc and won't transform our test files due to "exclude"
if (swcJestConfig.swcrc === undefined) {
swcJestConfig.swcrc = false;
}
// jest needs EsModule Interop to find the default exported function
swcJestConfig.module.noInterop = false;
export default {
globalSetup: '<rootDir>/src/global-setup-swc.ts',
transform: {
'^.+\\.[tj]s$': ['@swc/jest', swcJestConfig],
},
// other settings
};
```
```typescript {% fileName="global-setup-swc.ts" %}
import { registerTsProject } from '@nx/js/src/internal';
const cleanupRegisteredPaths = registerTsProject('./tsconfig.base.json');
export default async function () {
// swc will hoist all imports, and we need to make sure the register happens first
// so we import all nx project alias within the setup function first.
const { yourFancyFunction } = await import('@some-org/my-util-library');
yourFancyFunction();
// make sure to run the clean up!
cleanupRegisteredPaths();
}
```
## More Documentation
- [Jest Docs](https://jestjs.io/)