152

I am using Jest to test an angular app and it is taking a really long time for simple tests to run and I can not seem to figure out why.

My Jest setup in package.json:

"jest": {
  "modulePaths": [
    "<rootDir>/src",
    "<rootDir>/node_modules"
  ],
  "testPathIgnorePatterns": [
    ".git/.*",
    "node_modules/.*"
  ],
  "transformIgnorePatterns": [
    "node_modules/.*",
    ".*\\.js"
  ],
  "setupTestFrameworkScriptFile": "<rootDir>/src/setupJest.js",
  "preset": "jest-preset-angular",
  "testEnvironment": "jsdom",
  "testRegex": "src/app/.*\\.spec\\.ts$",
  "moduleFileExtensions": [
    "ts",
    "js",
    "json"
  ],
  "verbose": true,
  "cacheDirectory": ".jest-cache",
  "coveragePathIgnorePatterns": [
    ".*\\.(shim\\.ngstyle|ngfactory)\\.ts"
  ],
  "globals": {
    "ts-jest": {
      "tsConfigFile": "./tsconfig.json"
    },
    "__TRANSFORM_HTML__": true
  }
}

My Jest setup file:

'use strict';
require('core-js/es6/reflect');
require('core-js/es7/reflect');
require('zone.js');
require('zone.js/dist/proxy.js');
require('zone.js/dist/sync-test');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');
require('jest-zone-patch');

const getTestBed = require('@angular/core/testing').getTestBed;
const BrowserDynamicTestingModule = require('@angular/platform-browser-dynamic/testing').BrowserDynamicTestingModule;
const platformBrowserDynamicTesting = require('@angular/platform-browser-dynamic/testing')  .platformBrowserDynamicTesting;

getTestBed().initTestEnvironment(
    BrowserDynamicTestingModule,
    platformBrowserDynamicTesting()
);

Here is my simple test:

fdescribe('RichTextEditorComponent', () => {
  it('should be fast', () => {
    expect(true).toBeTruthy();
  });
});

Does anyone have any idea as to why this is taking 9+ seconds? enter image description here

Vijay Gaikwad
  • 457
  • 1
  • 4
  • 16
Tucker
  • 1,722
  • 2
  • 10
  • 12
  • 1
    Using Windows 7 - 64bit / node 6.9.4 / npm 3.10.10 – Tucker Jul 13 '17 at 17:35
  • Did you find out why? – RClemens Feb 14 '18 at 21:53
  • 2
    @RClemens yes - it has to do with the package of jest itself being slower on windows machines - if I recall correctly. – Tucker Mar 12 '18 at 13:17
  • 1
    Might be related to [this issue](https://github.com/facebook/jest/issues/6783) or [this issue](https://github.com/facebook/jest/issues/6694). If you're on watch mode, disabling it might save you a couple seconds. Apparently, running the tests sequentially improves the performance by 50% on some VMs. You might want to try this too, by adding the `--runInBand` flag. – Antoine Boisier-Michaud Jan 28 '19 at 21:20
  • 2
    The only configuration-related reason I can find is you're using `setupTestFrameworkScriptFile`, which runs before **each test**. You should be using the `setupFiles` setting, which is an array of strings. One of those strings should point to your file, and it will run for each spec file instead of running for each test. https://jestjs.io/docs/en/configuration#setupfiles-array – fnune Feb 05 '19 at 18:28
  • This is a simple solution as described [here](https://stackoverflow.com/questions/47691705/angular-4-unit-tests-testbed-extremely-slow) – It-Z Apr 04 '19 at 14:43
  • Possible duplicate of [Angular 4 Unit Tests (TestBed) extremely slow](https://stackoverflow.com/questions/47691705/angular-4-unit-tests-testbed-extremely-slow) – It-Z Apr 07 '19 at 06:55
  • [Clearing the cache](https://stackoverflow.com/questions/44866994/how-can-i-clear-the-jest-cache) did it for me. – pseudosudo Oct 05 '21 at 20:47
  • 1
    __WSL2 Users:__ Install jest globally too (in addition to installing it as a dev dependency) and then use its binary's absolute path in the `package.json` file instead, e.g.: `"test": "/usr/bin/jest",`. – aderchox Dec 16 '21 at 16:36
  • Do you have import in your test file? This can cost a lot because it seems that babel/typescript do their job at this moment and are often the difference between the time of one test vs the describe (of the very first test that is slow vs nexts very fast) – romuleald Feb 17 '22 at 14:24

15 Answers15

144

Another possibility is that ts-jest is slow. There was an issue about that, and it was not completely resolved.

There are various workarounds discussed. They consist of setting isolatedModules=true and also --maxWorkers=1. That is, in jest.config.js

'use strict';

module.exports = {
    preset: 'ts-jest',
    testEnvironment: 'node',
    globals: {
        'ts-jest': {
            isolatedModules: true
        }
    },
}

and run

yarn test --maxWorkers=1

Could be worth trying. Alternatively, it is possible to forgo ts-jest and use babel transpilation.

halfer
  • 19,824
  • 17
  • 99
  • 186
user7610
  • 25,267
  • 15
  • 124
  • 150
  • 8
    Had slow tests on a Linux machine as well. `isolatedModules` was the key. Did not set `maxWorkers=1` – erjitka Oct 21 '21 at 08:45
  • isolatedModules cause error with Integration Test, maxWorkers is effective instead. – Tan Nguyen Nov 24 '21 at 04:23
  • Quick thought: what about running the tests in Deno, instead of NodeJS? It should be able to run TypeScript without preprocessing... I did a quick search and it does not look like people actually (can?) do this; here's somebody mentioning Deno while complaining Jest is slow, but I think that was simply for timing comparison https://github.com/facebook/jest/issues/7963 – user7610 Apr 11 '22 at 17:07
  • 8
    Beware that `isolatedModules: true` will disable type-checking – Maxim Mazurok Jun 28 '22 at 06:00
  • 2
    @MaximMazurok [source](https://huafu.github.io/ts-jest/user/config/isolatedModules) – ynn Jul 09 '22 at 01:50
  • 4
    As of `ts-jest` 29, setting this option under the `globals` key appears to be deprecated. [Now](https://kulshekhar.github.io/ts-jest/docs/getting-started/options/isolatedModules) it seems you set a `transform` option. – Ethan Kent Sep 23 '22 at 20:23
  • 1
    `--maxWorkers=1` worked for me, as of 2022. `isolatedModules` did nothing. It actually increased my speed from `3.7s` to `4.2s` – GROVER. Nov 28 '22 at 04:28
  • Using the `isolatedModules` fix reduced the disk IO of the AWS instance from >1TB to just about 200MB for our full test run. That's a factor 5000! I guess underlying it must be a matter of parallel disk IO, since later tests revealed that the `--runInBand` also managed to reduce disk IO significantly. (but not as much as the isolatedModules did). Note, we didn't have to put it in a `transform`, it worked fine under the `global` config even though we're on a recent ts-jest version. – bvdb Feb 27 '23 at 12:51
30

Read these two links:

https://itnext.io/how-to-make-your-sluggish-jest-v23-tests-go-faster-1d4f3388bcdd https://github.com/facebook/jest/issues/7963

Here's a list of things to consider. They aren't specific to your case, but since the title of the question is quite general I thought they might help some percentage of visitors. They shouldn't be tried blindly, they are simply a starting point to research.

Things to try to speed up your jest tests:

  1. Run in watch mode with --watch

    jest optimizes when you use --watch.

  2. run on your host computer instead of in docker? -> I was previously using docker exec -it <containername> yarn test and found it faster when I changed to using my host.

  3. upgrade jest version it seems like there were some bugs that made some versions slower https://github.com/facebook/jest/pull/8046

    note: that yarn upgrade obeys the ~ and ^ version signifiers, if you know what you're doing, you might just want to remove and re add yarn remove jest yarn add -D jest that will just get you the latest

  4. change the test environment from jsdom to node

"jest": {
  "testEnvironment": "node"
}
  1. Run the tests syncronously.. allows jest to optimize?

add --runInBand option

  1. Setting max workers might make it faster?

add --maxWorkers=4 option

In my case I upgraded the jest version, started using --watch and --runInBand and running on my host instead of via docker, and my test time went from 2 mins to 10 seconds. I don't know what the problem was exactly in my case.

Julian Orinyol
  • 576
  • 5
  • 4
  • changing the `testEnvironment` to `node` reduces the startup time a lot – Wong Jia Hau Dec 09 '20 at 11:07
  • Watch mode was super slow for me, but adding --runInBand fixed it. Seemed like it was taking ~20 seconds for the worker pool to initialize before?? My test suite is small, so I didn't really need parallelization anyways. – Caleb Miller Jan 31 '21 at 02:19
  • 17
    Both --runInBand and --maxWorkers do not make sense together. – Alexey Sh. Feb 04 '21 at 10:02
  • 3
    `--runInBand` fixed it for me. – Nick Grealy Mar 22 '21 at 14:01
  • 1
    Setting `testEnvironment: node` broke all my tests because window was undefined and `runInBand` only seems to offer improvements when there are a few tests but slows things down greatly with 300 Suites running. `maxWorkers` can be useful if you're running inside a container that reports more CPUs than you actually have but didn't seem to do much in my case. – Jonathan Rys May 12 '21 at 14:58
  • Regarding runInBand you can also write a percentage like `maxWorkers: '25%'`. Might be better if you are sharing your repo with other people with different CPU specs. This was definitely faster for me than `runInBand`. – Seega Apr 25 '22 at 09:07
  • `--runInBand` speeds my tests up when working with single files (is usually the case during development). Also `testEnvironment: 'node'` speeds up. Im using Angular with NX both of Version 13.x. Adding `testEnvironment: 'node',` to the file `jest.config.js` in root worked for me. – Juri Sinitson May 11 '22 at 10:49
  • For the CI would use `testEnvironment: 'node'` only. I suppose `--runInBand` is not suitable for the CI. – Juri Sinitson May 11 '22 at 10:53
  • `testEnvironment` is dictated by the nature of the project. If it's a web project (and will be references web concepts like `document` and `window`), it should be `jsdom` or something that emulates the DOM. If it's a node app, it can be `node`. – Mark Feb 03 '23 at 17:51
24

2023 - Try this:

globals: {
  "ts-jest": {
    isolatedModules: true
  }
}

and run

yarn test
geraldoahnert
  • 349
  • 2
  • 4
24

Solution: SWC (Speedy Web Compiler)

ts-jest is most likely slowing down the tests.

SWC is drop-in alternative, written in Rust and super fast. More than 10x improvement for me. link

installation

# if you use npm
npm i -D @swc/core @swc/jest

# if you use yarn
yarn add -D @swc/core @swc/jest

Inside jest.config.js, configure Jest to use SWC:

module.exports = {
  transform: {
    "^.+\\.(t|j)sx?$": ["@swc/jest"],
  },
};

usage

yarn jest --runInBand

note: jest version 28 currently unstable. I use 27.0.4

Alternative: ESBuild

use can also use esbuild written in Go, also very fast. (similar performance to swc)

yarn add -D esbuild-jest esbuild

Inside jest.config.js, configure Jest to use esbuild

module.exports = {
  transform: {
    "^.+\\.(t|j)sx?$": ["esbuild-jest"],
  },
};

inspired by speeding-up-jest

Ali80
  • 6,333
  • 2
  • 43
  • 33
  • 1
    This approach much quicker than using ts-jest. I don't think there's any need to run typescript on every test run during development, considering your IDE will be doing real-time type checking. Just need to make sure typescript is run during explicit builds/CI to ensure type errors don't slip though. – Hal Feb 09 '23 at 07:04
  • thanks, here's my benchmark ts-jest without isolatedModules : 2.7sec / ts-jest with isolatedModules : 1.2 sec / swc/jest : 0.7sec – Vincent Mar 08 '23 at 05:56
3

I think the answer will ultimately need to come from the Angular team. The documentation for platformBrowserDynamicTesting is sparse (https://angular.io/api/platform-browser-dynamic/testing/platformBrowserDynamicTesting).

Perhaps platformBrowserDynamicTesting emulates a browser and loads the entire DOM for your application into memory. In this case, a nearly 10 second ramp up for an Angular application (without any cached JavaScript) seems reasonable. Maybe I am interpreting this wrong, but per your reports, it looks like the actual test is running in 6 milliseconds which seems like it should be fulfilling your requirement of a "fast test". I would be curious to see how long the tests take if you add another simple "should be fast 2" test. If the total is still under 10 seconds, that suggests your actual tests are taking very little time in comparison to the ramp up of the Angular platformBrowserDynamicTesting utility.

Rohit Sharma
  • 3,304
  • 2
  • 19
  • 34
phil
  • 3,538
  • 2
  • 24
  • 24
3

I solved same issue via installing jest as globally

npm install -g jest@26.0

here is some benchmark results with same project and same test cases

local - win10 version 2004 ----------------- -- node-14.7.0 -- 11.847 s

global - win10 version 2004 ----------------- -- node-14.7.0 -- 0.907 s

global - win10 version 2004 -- wsl/ubuntu-18.04 -- node-14.7.0 -- 0.469 s

  • 1
    Makes no difference for me (files on Windows, jest executed in git-bash). Where are you running your tests and where are your source files stored? On Windows, or in the WSL? – TmTron Feb 06 '21 at 13:38
  • today I use windows10 + WSL2-ubuntu and speed is acceptable. Test files are in windows partition. I think using tools like nvm or running jest locally can be reason. But I do not have solid proof about it. – mustafa kemal tuna Feb 07 '21 at 05:40
  • 4
    Although this answer has multiple downvotes, I've upvoted it. This is exactly the case. WSL2 has always recommended (officially) that accessing files within the Linux filesystem will have better performance compared to accessing Windows filesystem from the Linux VM. This *might be* an example of that. To some extent it doesn't make sense though, because even installing the jest globally will add its binary in the Linux filesystem (/usr/bin/jest in my case), but still for whatever reason, it works 10 times faster. __The gist of it:__ In order to use it with npm, use: `"test": "/usr/bin/jest",`. – aderchox Dec 16 '21 at 16:34
  • installing globally and uninstalling locally did helped me to reduce time from 3.7s to 0.15s and also time before the tests start dramatically, I also use wsl2 with ubuntu and files stored on windows – s0up Jun 09 '22 at 00:13
2

I also use Jest on my Angular project and I'm not sure that is a good solution.

When you configure your testing module, you can use NO_ERRORS_SCHEMA and you don't have to add all nested components in declarations to compile the component that you want to test.

beforeEach(async () => {
    return TestBed.configureTestingModule({
      declarations: [
        MyComponent
      ],
      schemas: [NO_ERRORS_SCHEMA]
    }).compileComponents();
  });

Your tests with Jest are unit tests, so with that solution, you will only test your component. If you want to test interaction between components, you will do end-to-end tests with Protractor or Puppeteer.

d219
  • 2,707
  • 5
  • 31
  • 36
2

Took a while for me to hunt down the bugger

Eventually what worked for me is to profile the jest test and look through to identify what is causing the slow jest startup. You can use this video to help.

For me it was the @mui/icons-material library. after uninstalling it, running a single file went from 5s to 2s.

Alternative I found:

// Instead of destructuring like such:
import { ExpandMore } from "@mui/icons-material"

// Directly importing speeds up by 3s
import ExpandMore from "@mui/icons-material/ExpandMore"

This process can help you identify the root cause, but this is ultimately not a fix.

related: https://github.com/mui/material-ui/issues/12422

related: https://github.com/facebook/jest/issues/10833#issuecomment-1192232331

Mingsheng
  • 1,031
  • 10
  • 16
  • This!!! This is exactly what my issue was. When i stopped importing named icons and started importing the defaults from their own directories my testing time went for 534s to 83s. – Stuart Mar 31 '23 at 15:57
1

My tests were running very slow due to this issue with faker (version: 7.3.0).

Using const { faker } = require('@faker-js/faker/locale/en_US'); instead of const { faker } = require('@faker-js/faker'); sped up the require statement by about 50 seconds.

shmuels
  • 1,039
  • 1
  • 9
  • 22
0

In case anyone dealing with slow execution of jest test suites, Upgrade the version to 25 or above. Jest version 24 runs slow.

https://jestjs.io/blog/2020/01/21/jest-25#performance-improvements[jest-25#performance-improvements][1]

0

Using docker and having a volume mounted for node_modules caused the tests to be very slow.

Removing the mounted volume on node_modules sped up the tests by about 60 seconds.

See the following links for more details on this.

  1. https://stackoverflow.com/a/47564342/9530790
  2. https://stackoverflow.com/a/49081609/9530790
shmuels
  • 1,039
  • 1
  • 9
  • 22
0

Having a file watcher while using jetbrains slowed my tests by about 100%.

I noticed this when I closed down my jetbrains ide and my tests ran over 100% faster.

shmuels
  • 1,039
  • 1
  • 9
  • 22
0

Downgrading form jest ^29.x.x to ^26.6.3 more than doubled my jest speed to a more reasonable time of a few seconds instead of double digit seconds.

lwdthe1
  • 1,001
  • 1
  • 16
  • 16
  • 1
    The problem is that Jest has a memory leak in Node16.1+, and the workaround for it is in Jest29. This memory leak made our CI take 40 minutes instead of 30, so upgrading from Jest26 to Jest29 made us faster. – Ivan Rubinson Aug 31 '23 at 12:10
0

Just in case, if your test cases are taking too long because of the hardware you are using.

You may get the error:

  thrown: "Exceeded timeout of 5000 ms for a hook.
  Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."

To avoid this error you can increase the timeout in the command line using the --testTimeout <TIME_IN_MILLI_SECONDS> option e.g. here I've used the 50 seconds

jest --testTimeout 50000

Note: The default timeout is 5000 ms (5 seconds).

Asad Shakeel
  • 1,949
  • 1
  • 23
  • 29
0

If you are collecting coverage, make sure you are excluding the node_modules folder. Otherwise, for any unit test it finds it will include the dependencies from node_modules in its analysis.

jest.config.js

module.exports = {
  verbose: true,
  collectCoverage: true,
  coveragePathIgnorePatterns: ['node_modules'],
  modulePaths: ['<rootDir>'],
  preset: 'jest-preset-angular',
  setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'],
};
Casey Plummer
  • 2,629
  • 23
  • 20