I have a big React app with 4205 Jest test running in a CI and it is taking more than 40 minutes to run all the tests. This amount of time is normal?
Versions:
NPM: 7.24.0
Node: 16.10.0
@testing-library/jest-dom: 6.4.2
@testing-library/react: 12.1.5
@testing-library/user-event: 14.5.2
jest: 29.7.0
jest-canvas-mock: 2.3.1
jest-environment-jsdom: 29.4.0
jest-junit: 16.0.0
jest-sonar: 0.2.16
jest-transform-stub: 2.0.0
jsdom: 21.1.0
jest.config.js:
module.exports = {
clearMocks: true,
restoreMocks: true,
moduleNameMapper: {
//some options
},
setupFiles: ["<rootDir>/buildTools/define-deprecated-global.js"],
setupFilesAfterEnv: ["<rootDir>/buildTools/setupTests.js"],
transform: {
//some options
},
transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
testEnvironment: "jsdom",
testPathIgnorePatterns: ["/test.js"],
testTimeout: 250000,
coverageDirectory: "target/coverage",
collectCoverage: true,
coverageReporters: ["json", "lcov", "clover"],
reporters: [
//some entries
]
};
package.json script:
"test:ci": "node --max-old-space-size=8192 --experimental-vm-modules node_modules/.bin/jest --maxWorkers=20% --watchAll=false --silent",
Recently I tried to change package.json to this but the time increased:
"test:ci": "node --optimize-for-size --max-old-space-size=8192 --gc_interval=100 --concurrent-recompilation --no-compilation-cache --experimental-vm-modules node_modules/.bin/jest --maxWorkers=50% --watchAll=false --silent --ci",
Are there any recommendations to improve this? Any better configuration? Any better alternatives to Jest? I know that Node 20 has a fix for a memory leak, but I've already tried with Node 20, and the testing time is still huge.
Possible help:
no-compilation-cache
I followed this suggestion
https://dev.to/retyui/resolve-memory-leaks-caused-by-jest-with-nodejs-16x-and-18x-javascript-heap-out-of-memory-3md5 and just added --no-compilation-cache
to package.json but I don't see any improvement.
Static maxWorkers
I found --maxWorkers
doesn't work very well in a CI environment. I'm requesting 9000m (9 cores) of CPU to the CI build. And with --maxWorkers=20%
sometimes the tests run with 9 cores, sometimes with 6, sometimes with 4. So, if I'm requesting 9000m, probably is better to use --maxWorkers=9
or --maxWorkers=7
.
Coverage
I read somewhere that gathering test coverage during test execution can increase test execution time. So I'm thinking about moving test coverage gathering to a different stage of CI.
To do that, I believe, in the package.json I need to add another command and call it in the new CI stage: "test:coverage": "jest --coverage"
.
And in my jest.config.js, I need to disable coverage gathering during tests execution: collectCoverage: false
.
Promises
I have always extra care with promises because I know they can exponentially increase my tests time and they can cause multiple flaky tests. So I:
async-await
instead of .then()
;.then()
;await
for promises result. If I'm not sure if a function will return a promise or not, I check what kind of result it will return, and if is a promise, so I obviously need to await
for it;async
or / and await
if not necessary. I've noticed that if any async
or / and await
are used incorrectly in the code, it will cause some flakyness in the tests.To prevent some of those cases, I added these rules in my .eslintrc.json:
"require-await": "error",
"no-return-await": "error",
Another suggestion is to use this: https://www.npmjs.com/package/eslint-plugin-promise
A thing I'm wandering is if doing this can increase memory leaks (because I have many tests like this):
functionToBeTested() {
requestSomeThingToTheServer(123).then(apiResult => {
doSomething(apiResult);
doSomethingMore();
});
}
test("check the Server request", (done) => {
mockServerRequest((requestParams) => { //is mocking requestSomeThingToTheServer() action
expect(requestParams).toEqual(123);
done();
});
functionToBeTested();
});
According with this link https://betterprogramming.pub/the-4-types-of-memory-leaks-in-node-js-and-how-to-avoid-them-part-2-f21fbda5c33b:
Promises get stored in memory and they do not clear until they are either handled or rejected
So if I use done()
in the test during the promise execution, it memory will not clear and can be a problem?
Vitest
I'm strongly considering migrating to Vitest. People say it's faster than Jest and doesn't seem difficult to migrate:
React 18
I'm not using React 18 yet but I'm concern because of this: https://github.com/testing-library/react-testing-library/issues/1235
Seems the tests time will increase even more with React 18.
Some extra info
https://apidog.com/blog/jest-test-running-concurrently/ https://snird.medium.com/do-not-use-node-js-optimization-flags-blindly-3cc8dfdf76fd https://gist.github.com/stormwild/4bd3c1ec50ed055a363012a403b16365 https://www.npmjs.com/package/eslint-plugin-promise