Jest和Mongoose - Jest检测到已打开的句柄

17

我正在使用 jest 测试我的 node.js 应用程序,测试完成时一切正常,但我从 jest 得到了一个有关 open handles 的消息。任何见解?

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)
4个回答

10
在 Stack Overflow 和 GitHub 问题上寻找解决方案多个小时后,我发现了这个 GitHub 线程 https://github.com/visionmedia/supertest/issues/520,其中提供了多个解决方案。 对我最终有效的方法是在我的根目录中实现一个全局拆卸文件,如下所示:
// test-teardown-globals.js
module.exports = () => {
  process.exit(0);
};

通过略微调整我的jest.config.js

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

更新:请注意@SimonSimCity的评论,这个解决方案存在一些权衡。考虑使用Özgür Atmaca的答案,通过断开mongoose连接来解决问题。


1
太神奇了!你的回答让我免去了数小时的疯狂调试和在谷歌前三页寻找解决方案的时间。太有效了! - Ondiek Elijah
1
很高兴我能为你做到这件事:D - Fiehra
6
注意:使用此方法,即使测试失败,您的进程也将始终具有“0”作为返回代码!通常,命令在错误时会返回“1”。背景:大多数系统依赖于返回代码来确定命令是否成功运行。 - SimonSimCity
谢谢你的提醒!所以这个解决方案只适用于在我的电脑上进行本地测试,而不适用于在服务器上运行的管道测试,是吗?@SimonSimCity - Fiehra
2
@Fiehra 没错。我也会避免在本地使用它。我曾经吃过苦头,需要花费大量时间才能重新建立对 CI 流水线和流程的信任,这次事件之后... - SimonSimCity
如果你有打开的句柄(如Redis/Mongo数据库连接、Websockets等),请不要选择这个解决方案,而是应该关闭它们。这样做会比直接退出进程更加干净整洁。 - Gabriel Pichot

7

看起来您的mongoose连接在测试后仍然保持开启状态,请尝试以下方法之一:

  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);
    };
    

可以尝试一个或者多个组合方案。 由于无法查看您的代码,以下是我的解决方案。


这会导致“匹配器错误” https://stackoverflow.com/questions/68340573/closing-mongoose-connection-in-a-jest-test-causes-matcher-error @kenneth - Sh eldeeb
3
在添加了 mongoose.connection.close() 后,仍然出现了使用 mongoose.connect 时的 openHandles 错误。 - pavittarx
@pavittarx 确保您也完成了上面的第3步。在连接部分使用 async/await。 - Bruno da Silva
1
很遗憾,这在2022年对我没有起作用,错误仍然存在。 - Fiehra

4
我之前使用Mongoose遇到了同样的问题,我通过以下方式解决:
beforeAll(async () => {
  await mongoose.disconnect();
  await mongoose.connect(MONGODB_URL, MONGODB_OPTIONS);
});

我发现大多数“解决方案”都涉及计时器,但我不喜欢用计时器来解决问题,因此上述解决方法对我来说更自然,希望能帮到您。


在断开连接之前检查当前状态可能也是一个好主意。mongoose.connection.readyState - hurricane
1
我不知道这是如何工作的,但它确实可以工作。 - Grant mitchell

3

这与model.init函数有关,它返回一个Promise。快速修复方法是在创建模型时传递skipInit标志,如下所示:

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

skipInit是此函数的第四个参数。

但是在这种情况下,它不会初始化模型的索引,因此最好根据process.env.NODE_ENV设置此标志。

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


1
我刚刚开始了一个几乎没有模型的空项目,并且出现了相同的错误。所以这对我来说至少不起作用。 - gandra404
2
@Alex 我使用了你的答案,它有效地防止了jest对未完成的promise进行抱怨。然而,我不确定它是否存在其他问题。在我的实时数据库中,它正确地抱怨了重复的电子邮件,因为它是一个唯一的字段。但是在通过mockgoose测试数据库中,它没有出错。我只能假设这是因为设置了skipInit = false,但我仍在继续调查。 - Zief

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接