Jest - Simple tests are slow
Asked Answered
C

16

163

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

Catalinacatalo answered 13/7, 2017 at 17:4 Comment(11)
Using Windows 7 - 64bit / node 6.9.4 / npm 3.10.10Catalinacatalo
Did you find out why?Paraph
@Paraph yes - it has to do with the package of jest itself being slower on windows machines - if I recall correctly.Catalinacatalo
Might be related to this issue or this issue. 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.Antimasque
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. jestjs.io/docs/en/configuration#setupfiles-arraySynagogue
This is a simple solution as described hereKyliekylila
Possible duplicate of Angular 4 Unit Tests (TestBed) extremely slowKyliekylila
Clearing the cache did it for me.Tekla
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",.Googins
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)Ruyter
2024, jest v29, nodejs v18, the same problemTillfourd
T
151

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.

Tightwad answered 28/3, 2020 at 18:59 Comment(9)
Had slow tests on a Linux machine as well. isolatedModules was the key. Did not set maxWorkers=1Bergamot
isolatedModules cause error with Integration Test, maxWorkers is effective instead.Disengagement
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 github.com/facebook/jest/issues/7963Tightwad
Beware that isolatedModules: true will disable type-checkingYanirayank
@MaximMazurok sourceMercuri
As of ts-jest 29, setting this option under the globals key appears to be deprecated. Now it seems you set a transform option.Ailsa
--maxWorkers=1 worked for me, as of 2022. isolatedModules did nothing. It actually increased my speed from 3.7s to 4.2sEnneagon
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.Unifoliolate
As Reference: isolatedModules got me from 166s for 680 tests to 90s.Matronymic
G
31

Read these two links:

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.

Gift answered 5/3, 2020 at 13:40 Comment(9)
changing the testEnvironment to node reduces the startup time a lotStahl
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.Thurlow
Both --runInBand and --maxWorkers do not make sense together.Tillfourd
--runInBand fixed it for me.Christiechristin
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.Cockayne
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.Sturrock
--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.Wristlet
For the CI would use testEnvironment: 'node' only. I suppose --runInBand is not suitable for the CI.Wristlet
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.Stefanysteffane
Z
28

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

Zeph answered 2/7, 2022 at 16:49 Comment(4)
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.Vicegerent
thanks, here's my benchmark ts-jest without isolatedModules : 2.7sec / ts-jest with isolatedModules : 1.2 sec / swc/jest : 0.7secCordeelia
@swc/jest is worthless if you eventually need to mock some module use spy.on(). @swc/jest doesn’t implement @swc/jest correctly and you need to resort to these ugly workarounds: npmjs.com/package/jest_workaroundRomeu
@Romeu let's hope github.com/jestjs/jest/issues/9430 will get some attention, to remove need for workarounds. Meanwhile try write CJS/CTS as the npm package page suggests at the end.Tightwad
F
26

2023 - Try this:

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

and run

yarn test
Fidele answered 18/5, 2022 at 18:45 Comment(7)
More information on the why?Matronymic
Also curious why this makes things faster. On my end, running a single dummy test went from taking ~8s to ~2.5s. Thanks!Inchon
It skips the TS check + some other things: huafu.github.io/ts-jest/user/config/isolatedModulesFlexor
Should be the accepted answer. My simple ts tests dropped from 4-5 seconds to 1-2 seconds.Placenta
You sir deserve a medal.Leer
If you get a deprecation warning, use instead: transform: { "^.+\\.(t|j)sx?$": ['ts-jest', { isolatedModules: true }] }Hutchens
Would you mind to explain a bit the first piece? Where it should be placed?Tillfourd
N
7

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

Nemeth answered 22/7, 2022 at 6:56 Comment(1)
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.Dun
O
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.

Obumbrate answered 28/3, 2019 at 16:57 Comment(0)
C
3

I solved same issue via installing jest as globally

npm install -g [email protected]

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

Chroma answered 3/8, 2020 at 15:58 Comment(4)
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?Carthy
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.Chroma
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",.Googins
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 windowsItol
D
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.

Diallage answered 19/6, 2019 at 10:6 Comment(0)
S
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.

Status answered 14/7, 2022 at 16:9 Comment(0)
S
1

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.

Status answered 8/9, 2022 at 14:28 Comment(0)
S
1

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'],
};
Supinate answered 10/2, 2023 at 22:56 Comment(0)
M
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]

Maulmain answered 8/4, 2021 at 14:24 Comment(0)
S
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://mcmap.net/q/151795/-how-can-i-speed-up-node-js-react-startup-in-a-docker-container
  2. https://mcmap.net/q/151796/-running-webpack-dev-server-in-docker-is-significantly-slower-than-on-local-machine
Status answered 8/8, 2022 at 17:5 Comment(0)
L
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.

Laurasia answered 20/9, 2022 at 12:17 Comment(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.Fascia
P
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).

Pinchhit answered 25/11, 2022 at 20:19 Comment(0)
H
0

For speeding and js heap memory do two things

  1. add --workerIdleMemoryLimit=350 in jest command like below

jest --env=jsdom --verbose a --coverage --max-old-space-size=16384 --silent --logHeapUsage --workerIdleMemoryLimit=16384

  1. add isolatedModules: true in jest config transform property like below

    transform: { '^.+\.ts?$': ['ts-jest', { isolatedModules: true }] }

Helicograph answered 22/3 at 16:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.