Express +Jest. Test files are running sequentially instead of in parallel - node.js

I have an Express.JS server which uses jest and supertest as a testing framework.
It has been working excellently.
When I call my test npm script, it runs npx jest and all of my test files run in parallel.
However I ran my tests recently and they ran sequentially which takes a very long time, they have done this ever since.
I haven't changed any jest or npm configuration, nor have I changed my test files themselves.
Has anyone experienced this? Or is it possible that something in my configuration is incorrect?
jest.config
export default {
setupFilesAfterEnv: ['./__tests__/jest.setup.js'],
}
jest.setup.js
import { connectToDatabase } from '/conn'
// Override the dotenv to use different .env file
require('dotenv').config({
path: '.env.test',
})
beforeAll(() => {
connectToDatabase()
})
test('', () => {
// just a dummy test case
})
EDIT: Immediately after posting the question, I re ran the tests and they ran in parallel, without me changing anything. If anyone has any knowledge around this i'd be interested to get a second opinion

After intermittent switching between parallel and sequential for unknown reasons. I have found it work consistently by adding the --no-cache arg to the npx jest call.
See below where I found the answer
Github -> jest not always running in parallel

Related

Restore between tests with Hapi Lab

Context:
I am using Hapi Lab to run a bunch of tests.
I also have an additional set of files that export mock JSON data.
What's the issue:
Each of the tests manipulate the JSON files as part of their routine.
The first test always passes, because it's dealing with the 'clean' JSON data, whereas following tests sometimes fail because the previous test(s) modified the JSON when the previous test(s) ran.
Similarly, if I run tests specifically one file at a time, the output is fine. Whereas if I attempt to test a directory that contains multiple tests, I get failures due to the malformed JSON.
npm run lab /test-directory/test1.js // Works fine
npm run lab /test-directory/test2.js // Works fine
vs
npm run lab /test-directory //Fails
What's the question:
Is there a convenient way of getting Lab to 'restore' the original state between tests instead of allowing the same instance of JSON data to leak between different tests?
I found that a convenient workaround for this issue was to run each test in its own worker using node child processes.
I added a file run-tests-sequentially.js which calls the tests in turn:
const { promisify } = require('util');
const { exec } = require('child_process');
const execAsync = promisify(exec);
(async () => {
console.log('Running test1...');
const { stdout: test1Output } = await execAsync('npm run test:integration /test-directory/test1.js');
console.log('Completed test1...');
console.log(test1Output);
console.log('Running test2...');
const { stdout: test2Output } = await execAsync('npm run test:integration /test-directory/test2.js');
console.log('Completed test2...');
console.log(test2Output);
})();
I then called node ./run-tests-sequentially.js to run the sequence.
Each test ran in a 'clean' worker, with no data leaks.

why is jest not required?

I have a react app and I don't know why I don't need to require the jest module.
import Task from './Task';
describe('class Task', () => {
it('inProgress()', () => {
var t = new Task("prova");
expect(t.isInProgress()).not.toBeTruthy();
});
});
The test command for create-react-app runs react-scripts test --env=jsdom.
The script for react-scripts test requires jest on this line and after configuring everything it runs jest on this line.
jest then finds your test files, loads them, and runs them.
In other words, your tests don't load and run jest, it's jest that loads and runs your tests.
Since your tests run within jest they can take advantage of the globals, expect, environment, etc. provided by jest without having to "require or import anything to use them".

Jest - do something before every test

Jest provides some useful methods for executing something before your tests: beforeEach() and beforeAll(). Jest docs on setup
The issue with these is they can only be placed inside of a describe block. So if I have many files, each with their own describe block, I need to place to beforeEach() in every file.
How can I run some code before & after every test, while only adding it once (instead of adding it in every describe block)?
You can try the globalSetup Jest config key. It is an optional key and it can be used to run an async function once before all test suites.
Please see https://github.com/facebook/jest/blob/master/docs/Configuration.md#globalsetup-string
Example of setting up globalSetup in package.json:
jest: {
globalSetup: "./path-to-global-setup.js"
}
... or in jest.config.js:
module.exports = {
globalSetup: "./path-to-global-setup.js"
};
Example of global-setup.js file:
module.exports = async function() {
// do something
};
This file will not be transformed by babel when you run your test suite.

Console.log statements output nothing at all in Jest

console.log statements output nothing at all in Jest. This was working for me yesterday, and all of sudden, it's not working today. I have made zero changes to my config and haven't installed any updates.
I'm not using the --forceExit option. Still seeing this issue.
Jest suppresses the console log message by default. In order to show the console log message, set silent option to false at the command line
set --silent=false in the command line:
npm run test -- --silent=false
You can run both options together like this --watch --verbose false if you want to also be watching the files and see the output.
for one time runs just do --verbose false
As per comment on https://github.com/facebook/jest/issues/2441,
Try setting verbose: false (or removing it) in the jest options in package.json.
This is a pretty old question and still there's no accepted answer. However, none of the suggested solutions worked for me (settings like --silent --verbose etc.). The main problem is that Jest changes the global console object. So, the easiest solution is to not use the global console object.
Instead import dedicated log functions from the console module and work with those:
import { error } from "console";
error("This is an error");
As easy as that.
Try using console.debug() instead.
Run console.debug('Message here', yourValueHere) inside test function and it should show in the console output when running test script. You can verify if it works using Ctrl+F and find Message here in the standard output.
This does the trick of showing output in the console, while it is not an answer quite on how to use console.log I understand.
I am running #testing-library/jest-dom and jest-junit 12.0.0 as devDependencies.
jest-junit has a minimal configuration of
"jest-junit": {
"usePathForSuiteName": "true"
},
in package.json. This is mainly to configure coverage reporting.
jest is configured like this:
"jest": {
"testMatch": [
"**/__tests__/**/*.[jt]s?(x)",
"**/?(*.)+(spec|test).[jt]s?(x)",
"!**/utilities.ts",
],
Check for your command line flags in package.json to see that you don't have --silent in there.
in addition to --verbose option which can cause this as mentioned, be aware that the --watch may also cause this bug.
One of the potential reason that logging is not printing is due to console.log has been mocked. Something as below
// jest-setup.js
global.console = {
// eslint-disable-next-line no-undef
log: jest.fn(), // console.log are ignored in tests
// log: console.log,
// Keep native behaviour for other methods, use those to print out things in your own tests, not `console.log`
error: console.error,
warn: console.warn,
info: console.info,
debug: console.debug,
};
// package.json
"jest": {
"preset": "react-native",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
],
"setupFilesAfterEnv": [
"#testing-library/jest-native/extend-expect",
"<rootDir>/src/config/jest-setup.js"
],
"testMatch": [
"<rootDir>/src/**/__tests__/**/*.test.{ts,tsx}"
]
},
This is commonly used if you wish to disable console.log in jest
Also be sure that your jest config does not have silent: true. In my case, I didn't realize that someone else had added that to our config.
I don't see it in the list of config options, but the command line flag is documented here.
If using Webstorm with Jest configuration, click on the file name instead of the test name.
Having tried a few of the config options in the previous replies, using console.debug() instead of console.log() worked.
In my case, the issue was caused by [only] flag in:
it.only() or test.only('some text',()=>{})
According to the v27 docs silent is what you want here. verbose false (the default) prevents Jest from outputting the result of every test in a hierarchy while silent true (the default) will:
Prevent tests from printing messages through the console.
Use npx jest --silent false if you want to run Jest with that option from the CLI. Tested this just now with console.log and it works as expected.
Tried the advice given regarding jest config settings to no avail. Instead, in my case, the issue seemed related to not awaiting asynchronous code:
test("test", async () => {
console.log("Does output")
new Promise(resolve => {
// some expectation depending on async code
setTimeout(() => resolve(console.log("Does not output")) , 1)
})
})
Rather, awaiting the promise does output the async log:
test("test", async () => {
console.log("Does output")
await new Promise(resolve => {
// some expectation depending on async code
setTimeout(() => resolve(console.log("Does output")) , 1)
})
})
Possibly related background:
https://github.com/facebook/jest/issues/2441
Try using console.info() which is an alias for console.log(). I tried almost all the above answers but still console.log() didn't worked for me by any means. So, used console.info() which did the work.
This is what worked for me: jest --verbose true
In my case the problem was that the logs where made when the module is required, so before the start of an actual test case. Change from a top-level import to using require inside the test case fixed the problem.
In my case the problem was importing the functions from the compiled version (present in dist folder) instead of the src folder. And therefore it was using the old version. So rebuilding the project and/or importing from src fixed my issue.
On MacOS with jest version 26.6.3 I had to append --silent="false"
renaming my file to index.test.js from index.spec.js did the trick for me.

Why can't get the global variable which was set in globalSetup in test code?

I use Jest to do unit test in node.
And I use the new feature globalSetup which come in Jest v22.
I have defined a global variable in globalSetup.
But I can't get it in the test code. Console log is undefined.
Anyone in this question?
Thanks.
Jest version: 22.0.0
node version: 8.9.0
yarn version: 1.3.2
OS: mac High Sierra 10.13.2
The code as follow:
// package.json
{
"jest": {
"globalSetup": "<rootDir>/src/globalSetupTest.js"
}
}
// globalSetupTest.js
module.exports = async function() {
global.foo = 'foo';
console.log(`global setup: ${global.foo}`);
};
// App.test.js
describe('APP test', () => {
it('renders without crashing', () => {
console.log({ foo: global.foo });
});
});
// test result
yarn run v1.3.2
$ node scripts/test.js --env=node --colors
global setup: foo
PASS src/App.test.js
APP test
✓ renders without crashing (5ms)
console.log src/App.test.js:3
{ foo: undefined }
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.354s, estimated 1s
Ran all test suites.
There's a solution offered from Jest people themselves: https://jestjs.io/docs/en/puppeteer.html. Note that if you're using CRA, this won't work out of the box (solution below), cause it currently doesn't support testEnvironment option for Jest.
Anyways:
In Jest config file you setup paths to global setup, teardown and test environment scripts
In global setup script, you create a browser and write it's WSEndpoint to a file
In global teardown script, you simply close the browser
In testEnvironment script, you read WSEndpoint from the file you saved before and then use it to connect to the running browser - after this, browser is available in your tests by using a global variable
If you're using CRA, you can use a custom setup for these tests and run them completely separately. And if you're using Puppeteer for e2e tests, this is probably what you want to do anyway.
You just add another script to your package.json: "test:e2e": "jest -c ./jest-e2e.config.js" and set it up as you want. Now you will have npm run test for unit tests and npm run test:e2e for end to end tests.
For what I understood is a design decision of Jest because is considered a bad practice to share state across different tests. Tests run in parallel and they should keep their own state.
See:
https://github.com/facebook/jest/issues/6007#issuecomment-381743011
https://github.com/facebook/jest/issues/4118
Can you try..
global.foo = 'foo';
console.log(`global setup: ${global.foo}`);
(remove the exports)
You can try changing globalSetup to setupFiles. That one, won't expect a function.
https://facebook.github.io/jest/docs/en/configuration.html#setupfiles-array

Resources