Node.js 使用mongoose和supertest完成测试运行后一秒钟,Jest没有退出

Node.js 使用mongoose和supertest完成测试运行后一秒钟,Jest没有退出,node.js,mongodb,async-await,jestjs,supertest,Node.js,Mongodb,Async Await,Jestjs,Supertest,我正在学习使用NodeJS和MongoDB创建RestAPI,从他们提供的repo(这篇文章有很多更新),我进一步修改它,用Jest和SuperTest对我的Mongo服务器进行单元测试 在加入我的回购协议之前,我对回购协议所做的主要修改是: 将侦听逻辑移动到start.js,而在package.json中,将其作为主文件 start.js const { app } = require("./index"); // Setup server port var port =

我正在学习使用NodeJS和MongoDB创建RestAPI,从他们提供的repo(这篇文章有很多更新),我进一步修改它,用Jest和SuperTest对我的Mongo服务器进行单元测试

在加入我的回购协议之前,我对回购协议所做的主要修改是:

将侦听逻辑移动到
start.js
,而在
package.json
中,将其作为主文件

start.js

const { app } = require("./index");

// Setup server port
var port = process.env.PORT || 8080;

app.listen(port, function () {
    console.log("Running RestHub on port " + port);
});

module.exports = {app};
app.use('/api', apiRoutes);
// Launch app to listen to specified port
// app.listen(port, function () {
//     console.log("Running RestHub on port " + port);
// });

module.exports = {app};
module.exports = {
  "preset": "@shelf/jest-mongodb"
};
const {MongoClient} = require('mongodb');
const {app} = require('./index'); // Link to your server file

const supertest = require('supertest');
const request = supertest(app);
const mongoose = require("mongoose");
const Contact = require("./contactModel");



describe('insert', () => {

  let connection;
  let db;

  beforeAll(async (done) => {
    connection = await MongoClient.connect(global.__MONGO_URI__, {
      useNewUrlParser: true,
    });
    db = await connection.db(global.__MONGO_DB_NAME__);
    Contact.deleteMany({}, (err) => {
      done();
    });

  });

  afterAll((done) => {

    console.log("After all?");
    // await connection.close();
    mongoose.connection.close();
    // await db.close();
    done();
  });

  it('should insert a doc into collection', async (done) => {

    try {
      const item = {};
      item["name"] = "John 3";
      item["email"] = "john@example.org";
      item["phone"] = "12345678";
      item["gender"] = "Male";

      const response2 = await request
        .post('/api/contacts')
        .send(item);
      console.log(response2.status);
      console.log("Response2 done");


      const response = await request.get("/api/contacts");
      console.log(`Weird response status is ${response.status}`);
      expect(response.status).toBe(200);
      expect(response.body.data.length).toBe(1);

      done();

    } catch (error) {
      console.log(error);
    }
  }, 30000);
});
移动逻辑后,通过导出将应用程序包装到index.js中

index.js

const { app } = require("./index");

// Setup server port
var port = process.env.PORT || 8080;

app.listen(port, function () {
    console.log("Running RestHub on port " + port);
});

module.exports = {app};
app.use('/api', apiRoutes);
// Launch app to listen to specified port
// app.listen(port, function () {
//     console.log("Running RestHub on port " + port);
// });

module.exports = {app};
module.exports = {
  "preset": "@shelf/jest-mongodb"
};
const {MongoClient} = require('mongodb');
const {app} = require('./index'); // Link to your server file

const supertest = require('supertest');
const request = supertest(app);
const mongoose = require("mongoose");
const Contact = require("./contactModel");



describe('insert', () => {

  let connection;
  let db;

  beforeAll(async (done) => {
    connection = await MongoClient.connect(global.__MONGO_URI__, {
      useNewUrlParser: true,
    });
    db = await connection.db(global.__MONGO_DB_NAME__);
    Contact.deleteMany({}, (err) => {
      done();
    });

  });

  afterAll((done) => {

    console.log("After all?");
    // await connection.close();
    mongoose.connection.close();
    // await db.close();
    done();
  });

  it('should insert a doc into collection', async (done) => {

    try {
      const item = {};
      item["name"] = "John 3";
      item["email"] = "john@example.org";
      item["phone"] = "12345678";
      item["gender"] = "Male";

      const response2 = await request
        .post('/api/contacts')
        .send(item);
      console.log(response2.status);
      console.log("Response2 done");


      const response = await request.get("/api/contacts");
      console.log(`Weird response status is ${response.status}`);
      expect(response.status).toBe(200);
      expect(response.body.data.length).toBe(1);

      done();

    } catch (error) {
      console.log(error);
    }
  }, 30000);
});
package.json

{
  "name": "resthub2",
  "version": "2.0.0",
  "description": "A Node App demonstrating simple RESTFul API implementation",
  "main": "start.js",
  "scripts": {
    "test": "jest",
    "start": "node start.js"
  },
  "keywords": [
    "API",
    "resful",
    "json",
    "node",
    "mongodb",
    "express"
  ],
  "author": "David Inyang-Etoh",
  "license": "ISC",
  "dependencies": {
    "@shelf/jest-mongodb": "^1.2.3",
    "body-parser": "^1.19.0",
    "express": "^4.17.1",
    "jest": "^26.4.2",
    "mongoose": "^5.6.4",
    "supertest": "^5.0.0"
  }
}
我按照特殊说明将Jest和SuperTest连接到MongoDB,并添加了这两个文件

jest.config.js

const { app } = require("./index");

// Setup server port
var port = process.env.PORT || 8080;

app.listen(port, function () {
    console.log("Running RestHub on port " + port);
});

module.exports = {app};
app.use('/api', apiRoutes);
// Launch app to listen to specified port
// app.listen(port, function () {
//     console.log("Running RestHub on port " + port);
// });

module.exports = {app};
module.exports = {
  "preset": "@shelf/jest-mongodb"
};
const {MongoClient} = require('mongodb');
const {app} = require('./index'); // Link to your server file

const supertest = require('supertest');
const request = supertest(app);
const mongoose = require("mongoose");
const Contact = require("./contactModel");



describe('insert', () => {

  let connection;
  let db;

  beforeAll(async (done) => {
    connection = await MongoClient.connect(global.__MONGO_URI__, {
      useNewUrlParser: true,
    });
    db = await connection.db(global.__MONGO_DB_NAME__);
    Contact.deleteMany({}, (err) => {
      done();
    });

  });

  afterAll((done) => {

    console.log("After all?");
    // await connection.close();
    mongoose.connection.close();
    // await db.close();
    done();
  });

  it('should insert a doc into collection', async (done) => {

    try {
      const item = {};
      item["name"] = "John 3";
      item["email"] = "john@example.org";
      item["phone"] = "12345678";
      item["gender"] = "Male";

      const response2 = await request
        .post('/api/contacts')
        .send(item);
      console.log(response2.status);
      console.log("Response2 done");


      const response = await request.get("/api/contacts");
      console.log(`Weird response status is ${response.status}`);
      expect(response.status).toBe(200);
      expect(response.body.data.length).toBe(1);

      done();

    } catch (error) {
      console.log(error);
    }
  }, 30000);
});
contactController.test.js

const { app } = require("./index");

// Setup server port
var port = process.env.PORT || 8080;

app.listen(port, function () {
    console.log("Running RestHub on port " + port);
});

module.exports = {app};
app.use('/api', apiRoutes);
// Launch app to listen to specified port
// app.listen(port, function () {
//     console.log("Running RestHub on port " + port);
// });

module.exports = {app};
module.exports = {
  "preset": "@shelf/jest-mongodb"
};
const {MongoClient} = require('mongodb');
const {app} = require('./index'); // Link to your server file

const supertest = require('supertest');
const request = supertest(app);
const mongoose = require("mongoose");
const Contact = require("./contactModel");



describe('insert', () => {

  let connection;
  let db;

  beforeAll(async (done) => {
    connection = await MongoClient.connect(global.__MONGO_URI__, {
      useNewUrlParser: true,
    });
    db = await connection.db(global.__MONGO_DB_NAME__);
    Contact.deleteMany({}, (err) => {
      done();
    });

  });

  afterAll((done) => {

    console.log("After all?");
    // await connection.close();
    mongoose.connection.close();
    // await db.close();
    done();
  });

  it('should insert a doc into collection', async (done) => {

    try {
      const item = {};
      item["name"] = "John 3";
      item["email"] = "john@example.org";
      item["phone"] = "12345678";
      item["gender"] = "Male";

      const response2 = await request
        .post('/api/contacts')
        .send(item);
      console.log(response2.status);
      console.log("Response2 done");


      const response = await request.get("/api/contacts");
      console.log(`Weird response status is ${response.status}`);
      expect(response.status).toBe(200);
      expect(response.body.data.length).toBe(1);

      done();

    } catch (error) {
      console.log(error);
    }
  }, 30000);
});
但是,我的测试不会终止,即使我将通过所有测试(仅为1),并给出以下消息

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.

我们尝试了runInBand中的大多数建议,转而关闭与mongoose的连接,但仍然没有终止。有人解决了我的问题吗?

如果没有正在进行的异步操作,也没有数据库连接和服务器侦听器等打开的处理程序,则不会发生此错误。这意味着每个连接都需要在
结束时关闭。关闭连接是异步操作,需要等待

由于没有导入start.js,因此不需要关闭服务器,Supertest
request(app)
会设置一个服务器并自动关闭它

Mongo和Mongoose API支持承诺,可以通过
async..wait
处理
async
不应与
done
混合使用,因为这是一种反模式,通常会导致不正确的控制流

如果存在默认的
mongoose.connect
connection,则需要关闭:

  afterAll(async () => {
    await mongoose.connection.close();
  });
如果存在非默认的
mongoose.createConnection
连接,那么它们也需要公开和关闭

Mongo连接在这里不可用,因为它与Mongoose连接不同。就像一场大扫除。如果需要Mongo(而不是Mongoose)连接,则需要显式关闭:

  afterAll(async () => {
    await connection.close();
  });

这可以在单独的
块中完成,以使失败的操作不会影响其他操作。

创建Mongo*连接,但关闭**Mongoose连接。而且Mongo
db
连接没有用于任何用途。@EstusFlask很抱歉这里有多余的代码。实际上,我已经尝试使用注释中的
connection.close()
以及旧的异步签名关闭Mongo*连接。这也不能解决问题。我同意
db
,应该清理bc,在我的测试之前,我不能让db清理,我用它代替
联系
导入。您看到我的情况的修复方法了吗?我不确定您的意思,您需要关闭测试单元(未显示)使用的相同连接。你把承诺和回访混为一谈对情况没有帮助。不要将
done
async
混用。Mongo(ose)基于承诺,可以与
async
配合使用。如果有两个连接(Mongoose default和Mongo
db
),它们都应该关闭,
等待Mongoose.connection.close();等待db.close()
。如果没有其他处理程序(如服务器连接),这就足够了。如果Mongoose使用非默认连接,则需要将其公开并关闭。基本上,每个打开的连接都需要在
结束时关闭(Supertest自动关闭自己的连接,不需要处理)。同样,
db
在代码中是无用的,应该被删除。如果仍然存在问题,请提供您的问题以使其易于理解。实际上,db.close()在他们看来甚至不是一个函数,但删除任何db痕迹似乎可以解决问题。你想回答这个问题吗?