Do you want to request a feature or report a bug?
bug :bug:
What is the current behavior?
mongodb
4.0.4 runningshutdown.test.js
with the following:const mongoose = require('mongoose')
const host = '10.0.8.83'
const name = 'test'
beforeAll(async () => {
mongoose.connect(`mongodb://${host}/${name}`, () => {
mongoose.connection.db.dropDatabase(() => {
mongoose.disconnect(() => {
console.log('closed')
})
})
})
})
test('a test', () => {
expect(true).toBe(true)
})
mongoose
5.3.11 and jest
23.6.0npx jest
What is the expected behavior?
The test to PASS and gracefully exit.
What happens?
The test PASSes successfully however it doesnt exit gracefully. jest output logs:
(node:16406) DeprecationWarning: current URL string parser is deprecated, and will be removed in a future version. To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.
PASS ./shutdown.test.js
✓ a test (2ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.174s
Ran all test suites matching /.\/shutdown.test.js/i.
console.log shutdown.test.js:8
closed
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.
Note: the above problem doesnt exist with mongodb 3
Digging into this, it turns out it is a problem with the jsdom test environment in Jest. Add this to your jest.config.js
and it should work fine:
module.exports = {
testRegex: 'gh-7240.js',
testEnvironment: 'node'
};
The problem is that the MongoDB driver is still waiting for connections to flush after it calls the disconnect()
callback, but Jest already removed the DOM so setTimeout()
silently fails, see this commit.
TLDR; don't use jest. But if you must use jest, make sure you use the node test environment. The default jsdom environment is very different from the runtime of an actual Node.js process because it is designed to mimic a browser, so you'll get a lot of cases where code works in Node but not jsdom and vice versa.
We'll see if we can get a PR in for fixing the issue with the MongoDB driver calling its callback before all operations are flushed
Hi @vkarpov15 and thanks for the prompt response :heart:
Actually now as you've explained in detail it makes a lot of sense and we'll definitely rethink our jest usage for nodejs tests...
We'll see if we can get a PR in for fixing the issue with the MongoDB driver calling its callback before all operations are flushed
Calling the callback of disconnect before all operations are flushed will do a side effect of calling the 'next logic' which could kill the process before operations are completed, thus gracefull shutdown will not happen (?) If I'm assuming correctly the case.
Thanks again, I'm closing the issue here as there is nothing else to be done here ;)
Also, somehow I missed this yesterday but you're not using async/await correctly and that also contributes to the issue:
beforeAll(async () => {
mongoose.connect(`mongodb://${host}/${name}`, () => {
mongoose.connection.db.dropDatabase(() => {
mongoose.disconnect(() => {
console.log('closed')
})
})
})
})
You're not actually awaiting on anything, so mongoose.connect()
and everything else happens after your tests are done. Do this instead:
beforeAll(async () => {
await mongoose.connect(`mongodb://${host}/${name}`);
await mongoose.connection.db.dropDatabase();
await mongoose.disconnect();
})
Shameless plug, but if you're not familiar with async/await I wrote a detailed ebook on the subject.
@vkarpov15 right. Sorry for that typo (it happend because the code has been extracted from a private repo) but even with your proposal to use only async/await things were not different :)
Now using latest "mongoose": "^5.3.13"
running the following:
const mongoose = require('mongoose')
const host = '10.0.8.83'
const name = 'test'
beforeAll(async () => {
await mongoose.connect(`mongodb://${host}/${name}`);
await mongoose.connection.db.dropDatabase();
await mongoose.disconnect();
})
test('a test', () => {
expect(true).toBe(true)
})
results in
(node:1804) DeprecationWarning: current URL string parser is deprecated, and will be removed in a future version. To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.
PASS ./shutdown.test.js
✓ a test (3ms)
console.warn node_modules/mongoose/lib/helpers/printJestWarning.js:4
Mongoose: looks like you're trying to test a Mongoose app with Jest's default jsdom test environment. Please make sure you read Mongoose's docs on configuring Jest to test Node.js apps: http://mongoosejs.com/docs/jest.html
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.219s
Ran all test suites.
there is no problem with jest been not able to gracefully exit :heavy_check_mark: , note that I didnt used jest's testEnvironment: 'node'
;)
adding the jest instruction for test env I have the same results without the warning.
I think this is what is expected to happen (esp. after this commit) and thank you for your prompt support :bowing_man: :heart:
Most helpful comment
Digging into this, it turns out it is a problem with the jsdom test environment in Jest. Add this to your
jest.config.js
and it should work fine:The problem is that the MongoDB driver is still waiting for connections to flush after it calls the
disconnect()
callback, but Jest already removed the DOM sosetTimeout()
silently fails, see this commit.TLDR; don't use jest. But if you must use jest, make sure you use the node test environment. The default jsdom environment is very different from the runtime of an actual Node.js process because it is designed to mimic a browser, so you'll get a lot of cases where code works in Node but not jsdom and vice versa.
We'll see if we can get a PR in for fixing the issue with the MongoDB driver calling its callback before all operations are flushed