17

So I'm using jest to test my node.js application and the tests finish fine but I'm getting a message from jest about open handles. Any insights?

jest --detectOpenHandles

PASS src/libs/user/tests/user_model_test.js PASS src/tests/app_test.js PASS src/libs/user/tests/user_service_test.js

Test Suites: 3 passed, 3 total Tests: 14 passed, 14 total Snapshots: 0 total Time: 7.209s Ran all test suites.

Jest has detected the following 4 open handles potentially keeping Jest from exiting:

● PROMISE

  2 | // we use a test database for testing
  3 | var mongoDB = 'mongodb://localhost/my_db_conn';
> 4 | mongoose.connect(mongoDB);
    |          ^
  5 | const User = require('../user_model');
  6 |
  7 | describe("User model test", () => {

  at NativeConnection.Object.<anonymous>.Connection.openUri (node_modules/mongoose/lib/connection.js:424:19)
  at Mongoose.Object.<anonymous>.Mongoose.connect (node_modules/mongoose/lib/index.js:208:15)
  at Object.<anonymous> (src/libs/user/__tests__/user_model_test.js:4:10)

● PROMISE

   8 | });
   9 |
> 10 | module.exports = mongoose.model("User", UserSchema);
     |                           ^

  at Function.init (node_modules/mongoose/lib/model.js:962:16)
  at Mongoose.Object.<anonymous>.Mongoose.model (node_modules/mongoose/lib/index.js:392:11)
  at Object.<anonymous> (src/libs/user/user_model.js:10:27)
  at Object.<anonymous> (src/libs/user/__tests__/user_model_test.js:5:14)

● PROMISE

   8 | });
   9 |
> 10 | module.exports = mongoose.model("User", UserSchema);
     |                           ^

  at Function.init (node_modules/mongoose/lib/model.js:962:16)
  at Mongoose.Object.<anonymous>.Mongoose.model (node_modules/mongoose/lib/index.js:392:11)
  at Object.<anonymous> (src/libs/user/user_model.js:10:27)
  at Object.<anonymous> (src/libs/user/index.js:1:41)

● PROMISE

  3 | var mongoose = require('mongoose');
  4 | var mongoDB = 'mongodb://localhost/my_db_conn';
> 5 | mongoose.connect(mongoDB);
    |          ^
  6 |
  7 | describe('App test', () => {
  8 |     it('has a module', () => {

  at NativeConnection.Object.<anonymous>.Connection.openUri (node_modules/mongoose/lib/connection.js:424:19)
  at Mongoose.Object.<anonymous>.Mongoose.connect (node_modules/mongoose/lib/index.js:208:15)
  at Object.<anonymous> (src/__tests__/app_test.js:5:10)
Canta
  • 1,480
  • 1
  • 13
  • 26
Rod
  • 14,529
  • 31
  • 118
  • 230

4 Answers4

9

after many hours of searching for a solution on SO and github issues, I came across this github thread https://github.com/visionmedia/supertest/issues/520 with multiple solutions offered. what was finally working for me was implementing a global-teardown-file in my root directory like so:

// test-teardown-globals.js
module.exports = () => {
  process.exit(0);
};

and also by adjusting my jest.config.js slightly

// jest.config.js
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  globalTeardown: '<rootDir>/test-teardown-globals.js',
};

update: please also note @SimonSimCity 's comment, this solution comes with some tradeoffs. Consider using Özgür Atmaca's answer by disconnecting the mongoose connection instead.

Fiehra
  • 659
  • 6
  • 23
  • 1
    Miraculous! Your answer has saved me from several hours of frantic debugging and searching for fixes up to page 3 of Google. Works!! – Ondiek Elijah Mar 23 '22 at 11:25
  • 1
    glad i was able to do that for you :D – Fiehra Apr 14 '22 at 08:54
  • 5
    **CAUTION:** By using this, your process will always have `0` as return code, even when having failing tests! Usually the command would return `1` on error. Background: Most systems rely on the return code to determine if a command ran successfully. – SimonSimCity May 03 '22 at 08:45
  • thanks for the headsup! so this solution is only viable for local tests on my computer but not for tests running on a pipeline on a server for instance? @SimonSimCity – Fiehra Aug 08 '22 at 21:39
  • 2
    @Fiehra exactly. I would also refrain from using it locally. I learned it the hard way and needed a tremendous amount of time to again gain trust in the CI pipelines and process after this incident... – SimonSimCity Aug 11 '22 at 07:07
  • Don't choose this solution, if you have open handle you should close them (databases connection like redis/mongo, websockets etc). It will far cleaner that exiting the process. – Gabriel Pichot Jul 25 '23 at 08:03
7

It seems your mongoose connection remains open after your test, try one of the following:

  1. close server instance after test.

    const server = require('./app'); //server instance    
    server.close(); //put in afterAll or afterEach depending on your test
    
  2. close your database connection after all your test.

    afterAll(()=>{ mongoose.connection.close();});
    
  3. wrap your mongoose connection with async/await.

    async function(){
       await mongoose.connect(mongoDB);
    };
    

try one or a combination. These are my solutions since I can't really see your code.

ascripter
  • 5,665
  • 12
  • 45
  • 68
4

I was having same issue with Mongoose and managed to fix it like this

beforeAll(async () => {
  await mongoose.disconnect();
  await mongoose.connect(MONGODB_URL, MONGODB_OPTIONS);
});

Most "solutions" I found was around timers and I am not a fan of fixing things with timers, so above workaround feels more natural to me, hope it helps.

Liger
  • 140
  • 2
  • 4
3

It's related to model.init function which returns promise. Quick fix will be to pass skipInit flag while creating the model like this:

const User = mongoose.model("users", userSchema, "users", true)

skipInit is the fourth parameter in this function

But in this case it will not initialize indexes for your model, so it's better to set this flag according to the process.env.NODE_ENV

const skipInit = process.env.NODE_ENV === "test" const User = mongoose.model("users", userSchema, "users", skipInit)

Alex Ni
  • 31
  • 2
  • 1
    I have just started with almost empty project without any model and having same error. So this at least not works for me. – gandra404 Jun 12 '18 at 12:39
  • 2
    @Alex I used your answer and it works - it prevents jest complaining about open promises. However, I'm not sure if it has other issues. In my live database, it correctly complains about duplicate email as it is a unique field. In my test database through mockgoose, it doesn't error. I can only assume because of setting the skipInit = false, but I am continuing to investigate – Zief Aug 07 '18 at 17:40