The Problem
You want to configure different test execution timeouts for Jest tests in different folders in the same project e.g. 1 second for tests in
./tests/unitTests/and 60 seconds for tests in./tests/integrationTests.-
During debugging, the timeout for tests in any folder should be quite large, say 10 minutes.
Otherwise Jest could throw timeout errors like
thrown: "Exceeded timeout of 5000 ms for a test.even if a test completes successfully. This happens when debugging a test takes longer than the (default or explicitly configured) timeout for it.
Solution
Given the folder structure:
.
│ jest.config.js
│ package-lock.json
│ package.json
│
└───tests
├───integrationTests
│ integrationTestSuite.test.js
│
└───unitTests
unitTestSuite.test.js
a solution is as follows:
npm install --save-dev debugger-is-attached-
Create a
jestSetup.jsfile in each of the test folders and set the desired timeout via a call tojest.setTimeout()method.
So we have atests/unitTests/jestSetup.jsfile:
jest.setTimeout(1000); //timeout of 1 secondand a
tests/integrationTests/jestSetup.jsfile:
jest.setTimeout(60000); //timeout of 1 minute -
Create
jest.config.jsin project root as follows
const { debuggerIsAttached } = require("debugger-is-attached"); const path = require("path"); module.exports = async () => { const isDebuggerAttached = await debuggerIsAttached(); const unitTestFolder = "<rootDir>/tests/unitTests"; const integrationTestFolder = "<rootDir>/tests/integrationTests"; const getSetupFiles = (folder) => isDebuggerAttached ? [] : [path.join(folder, "jestSetup.js")]; const baseProjectConfig = { //Here put any properties that are the same for //all folders and can be specified at level //of the project object (all such properties //are declared in type ProjectConfig in //Config.ts in Jest repo) }; let config = { //any config key/values in configuration (except those //that are to specified in ProjectConfig) //e.g. //collectCoverage: true, //project config projects: [ { ...baseProjectConfig, displayName: "UnitTests", testMatch: [path.join(unitTestFolder, "**/*.test.js")], slowTestThreshold: 1000, //1 second setupFilesAfterEnv: getSetupFiles(unitTestFolder), }, { ...baseProjectConfig, displayName: "IntegrationTests", testMatch: [path.join(integrationTestFolder, "**/*.test.js")], slowTestThreshold: 60000, //1 minute setupFilesAfterEnv: getSetupFiles(integrationTestFolder), }, ], //any other Jest config goes here //(these are any properties declared in type //InitialConfig but not in type ProjectConfig //in Config.ts in Jest repo) }; if (isDebuggerAttached) config["testTimeout"] = 600000; //ten minutes return config; }; -
In User Settings (
settings.jsonwhich appears when from the Ctrl + P command pallette you select Preferences: Open User Setting (JSON)), set"jest.monitorLongRun"to a value that is equal to or greater than the largest of the folder-specific timeouts declared injest.config.jsabove. For example,
"jest.monitorLongRun": 60000, //1 minute
Explanation
Configuring different timeouts for different test folders
testTimeout property can be set in jest.config.js in project root to set a timeout other than the default of 5s:
module.exports = {
...
testTimeout: 60000; //60 seconds
};
How to specify testTimeout separately for different subfolders?
The canonical way to assign different configurations to tests in different subfolders is to use Jest's monorepo configuration. We pretend, as far as Jest is concerned, that folders tests/integrationTests and tests/unitTests are two separate projects in a monorepo (a collection of related projects stored in a single repository).
Using this approach we can configure separate timeouts for our two subfolders as follows:
- Create a
jest.config.jsin each subfolder. In this settestTimeoutproperty as shown in the snippet above. -
Declare the different folder-specific config files as projects in the top-level
jest.config.js:
module.exports = { projects: [ "<rootDir>/tests/unitTests/jest.config.js", "<rootDir>/tests/integrationTests/jest.config.js", ], };
At this point the folder structure would be as follows:
.
│ jest.config.js
│ package-lock.json
│ package.json
│
└───tests
├───integrationTests
│ integrationTestSuite.test.js
│ jest.config.js
│
└───unitTests
jest.config.js
unitTestSuite.test.js
The problem is that if we configure testTimeout property in a folder-specific config file it will not have any effect.
This is because testTimeout is not defined in type ProjectConfig in Config.ts which specifies the config schema of project (which for use are the two subfolders of tests).
Even though the top-level as well as folder-specific config files are all called jest.config.js, the properties that are allowed in folder level config are not all the same as the properties allowed in top level config.
Folder-specific config files need to have be those defined in type ProjectConfig whereas top-level jest.config.js specifies properties that are probably declared in type InitialConfig in Config.ts.
Many keys are defined in both types but not testTimeout: it is only contained in InitialConfig and therefore only has effect if declared in the top-level config file.
Therefore testTimeout property cannot be use to override test timeouts in jest.config.js files in subfolders.
Instead, to set timeout at subfolder level:
- We can call
jest.setTimeout(TIMEOUT_IN_MS)in a.jsfile in the subfolder, conventionally namedjestSetup.js. - Declare
jestSetup.jsin the folder-leveljest.config.jsso that it would be run by Jest before any tests in that folder are executed.
For example create tests/integrationTests/jestSetup.js as follows:
jest.setTimeout(60000); //timeout of 1 minute
and a tests/integrationTests/jest.config.js to go with it:
module.exports = {
displayName: "IntegrationTests",
setupFilesAfterEnv: [`<rootDir>/jestSetup.js`],
};
For unit tests, ./tests/unitTests/jest.config.js would be the same as the config file for integration tests folder shown above (because <rootDir> always resolves to the containing folder). However ./tests/integrationTests/jestSetup.js would specify a different timeout:
jest.setTimeout(1000); //timeout of 1 second
The folder structure of this working solution looks like this:
.
│ jest.config.js
│ package-lock.json
│ package.json
│
└───tests
├───integrationTests
│ integrationTestSuite.test.js
│ jest.config.js
| jestSetup.js
│
└───unitTests
jest.config.js
jestSetup.js
unitTestSuite.test.js
We can eliminate folder-specific jest.config.js files by pulling the info in the top level config so the ./jest.config.js in the root now looks like this:
module.exports = {
projects: [
{
displayName: "UnitTests",
testMatch: ["<rootDir>/tests/unitTests/**/*.test.js"],
setupFilesAfterEnv: ["<rootDir>/tests/unitTests/jestSetup.js"],
},
{
displayName: "IntegrationTests",
testMatch: ["<rootDir>/tests/integrationTests/**/*.test.js"],
setupFilesAfterEnv: ["<rootDir>/tests/integrationTests/jestSetup.js"],
},
],
};
The folder structure of this more compact solution looks like this:
.
│ jest.config.js
│ package-lock.json
│ package.json
│
└───tests
├───integrationTests
│ integrationTestSuite.test.js
│ jestSetup.js
│
└───unitTests
jestSetup.js
unitTestSuite.test.js
I'd like to point out three improvements that can be made:
-
It is worth adding a
slowTestThresholdproperty in everyprojectobject and set it equal to or somewhat greater than the timeout declared in the correspondingjestSetup.js.A longer timeout prevents Jest from throwing an error on longer running tests. But it would still show the execution time of such a tests with a red background:
Adding
slowTestThreshold: 60000to the folder's config injest.config.jstells Jest that this is how long you expect tests in that folder to take so it doesn't show execution times in warning colour. -
If a property is exported both
ProjectConfigandInitialConfigtypes then, if you configure it in top leveljest.config.jsbut not in folder-level config, it would be overridden by project config to its default value which would probably benullorundefined.For example given a
jest.config.jsthat looks like this:
module.exports = { setupFiles = ['./topLevelSetupFile.js'], ... projects: [ { displayName: "IntegrationTests", testMatch: ["<rootDir>/tests/integrationTests/**/*.test.js"], setupFilesAfterEnv: ["<rootDir>/tests/integrationTests/jestSetup.js"], } ]; };In the effective folder-specific configuration for folder
./tests/integrationTests/,setupFilesproperty would be set to nothing (nullI think).A nice solution (from Orlando Bayo's post) is to set the shared config - properties that are shared across all folders - in a
baseProjectconfigobject and spread this into everyprojectobject.
Incorporating both of these improvements into our solution, we have the following jest.config.js:
const baseProjectConfig = {
//Here put any properties that are the same for
//all folders and can be specified at level
//of the project object (all such properties
//are declared in type ProjectConfig in
//Config.ts in Jest repo)
};
let config = {
//any config key/values in configuration (except those
//that are specified in ProjectConfig)
//e.g.
//collectCoverage: true,
//project config
projects: [
{
...baseProjectConfig,
displayName: "UnitTests",
testMatch: [path.join(unitTestFolder, "**/*.test.js")],
slowTestThreshold: 1000, //1 second
setupFilesAfterEnv: getSetupFiles(unitTestFolder),
},
{
...baseProjectConfig,
displayName: "IntegrationTests",
testMatch: [path.join(integrationTestFolder, "**/*.test.js")],
slowTestThreshold: 60000, //1 minute
setupFilesAfterEnv: getSetupFiles(integrationTestFolder),
},
],
//any other Jest config goes here
//(these are any properties declared in type
//InitialConfig but not in type ProjectConfig
//in Config.ts in Jest repo)
};
module.exports = config
-
Jest VS Code extension still shows a popup if a test takes too long to execute (although, because of the configuration above, the test won't fail on a timeout):
To prevent this warning window from popping up, set
"jest.monitorLongRun"in User Setting file (to edit it select Preferences: Open User Settings from Ctrl + P command pallette) to the value of the longest of your folder-specific timeouts e.g.:
"jest.monitorLongRun": 60000, //1 minute
Setting a long timeout when debugging
When debugging, if you take longer to step through a test than the (default or explicitly configured) timeout, Jest would throw a timeout error at the end even if the test completed successfully. I find that for a test to fail like this is confusing when you're debugging a test that you expect to pass.
To address this issue we can use the debugger-is-attached package. This exports an async function that returns true if debugger is attached and false otherwise.
It can be integrated into jest.config.js by exporting an async function that returns the config object instead of directly returning the object in question.
const { debuggerIsAttached } = require("debugger-is-attached");
module.exports = async () => {
//do async things
...
return {
//the config object
}
}
Inside the function, if the debugger is not attached then everything should be the same as before but if it is attached, then we make two changes to the returned object:
not configure
jestSetup.js, the file that declares the timeout for tests in its containing folder, to run.set
testTimeoutproperty to 10 minutes (600000ms) to give us plenty of time to debug a test.
After these changes, the final top-level jest.config.js is as shown in TL;DR at the top.
Thanks for reading. Any comments or suggestions for improvement would be greatly appreciated.



Top comments (0)