'JEST with Express does not finish
I started to write tests with Jest of (nano)express application. The test starts the server at beforeAll() and closes it at afterAll(). I can see that the code is executed, but the JEST process does not end.
test.js
test('end to end test', async () => {
const polls = await axios.get(`http://localhost:3000/bff/polls/last`);
console.log(polls.data);
expect(polls.data).toBeDefined();
});
beforeAll(() => {
app.listen(3000, '0.0.0.0')
.then(r => logger.info("Server started"));
});
afterAll(() => {
if (app.close())
logger.info("Server stopped");
});
Output from npm run test
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 5.625s
Ran all test suites.
Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.
When I run with jest --config jest.config.js --detectOpenHandles the test does not finish as well but there is no error and I need to kill it anyway.
The complete source code is there: https://github.com/literakl/mezinamiridici/blob/master/infrastructure/test/api.int.test.js
I have tested separatelly outside of the tests that nanoexpress will terminate the process with app.close() call. So it is JEST related.
Update: the same behaviour with promises
test('end to end test', () => {
const polls = axios.get(`http://localhost:3000/bff/polls/last`);
return expect(polls).resolves.toBeDefined();
});
Update:
Here you can find minimum reproducible repository: https://github.com/literakl/nano-options.git
I have switched from Axios to GotJS and the trouble is still there. When I run the test with npm run test from command line now, it fails with:
Timeout - Async callback was not invoked within the 20000ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 20000ms timeout specified by jest.setTimeout.Error
When I start the test from WebStorm there is no error but the process keeps running.
Solution 1:[1]
UPDATE
My initial thought was that this is a winston related issue but it appears that jest testEnvironment has to be set to node in order for Axios to run propertly using the axios/lib/adapters/http adapter. You can check a related issue here "detect jest and use http adapter instead of XMLhttpRequest".
- Set
testEnvironment: 'node'insidejest.config.js. - Update
create usertest to run thedonecallback function at the end:
describe("user accounts", () => {
test('create user', async (done) => {
// let response = await axios.get(`${API}/users/1234`);
let response = await axios.get(`${API}/users/1234`, getAuthHeader()); // TODO error with Authorization header
expect(response.data.success).toBeTruthy();
expect(response.data.data).toBeDefined();
let profile = response.data.data;
expect(profile.bio.nickname).toMatch("leos");
expect(profile.auth.email).toMatch("[email protected]");
// Call done here to let Jest know we re done with the async test call
done();
});
});
Solution 2:[2]
The root cause was an open handle of mongodb client. How did I find it?
1) install leakes-handle library
npm install leaked-handles --save-dev
2) import it to your test
require("leaked-handles");
3) the output
tcp handle leaked at one of:
at makeConnection (infrastructure\node_modules\mongodb\lib\core\connection\connect.js:274:20)
tcp stream {
fd: -1,
readable: true,
writable: true,
address: { address: '127.0.0.1', family: 'IPv4', port: 54963 },
serverAddr: null
}
If you cannot find the root cause, you can kill the JEST explicitelly with
jest --config jest.config.js --runInBand --forceExit
Solution 3:[3]
Here is another reason for me. I was using Puppeteer and because my target element was hidden the screenshot method threw error:
const browser = await puppeteer.launch({
headless: true,
executablePath: chromiumPath
});
const page = await browser.newPage();
await page.goto(`file://${__dirname}\\my.html`);
const element = await page.$("#my-element");
// This may throw errors
await element.screenshot({path: snapshotFileName});
await browser.close();
So, I made sure that the browser.close() was called no matter what:
try {
await element.screenshot({path: snapshotFileName});
} finally {
await browser.close();
}
Solution 4:[4]
Following works for my integration testing with Express, Nodejs, and Jest. Nothing special in package.json either: "test": "jest --verbose". Cheers
afterAll( async () => {
await mongoose.connection.close();
jest.setTimeout(3000);
});
Tests: 6 passed, 6 total Snapshots: 0 total Time: 4.818 s, estimated 5 s Ran all test suites matching /users.test.js/i.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Leos Literak |
| Solution 3 | Mahozad |
| Solution 4 | Gordon Graham |
