如何解决监听EADDRINUSE:集成测试中已经使用的地址

问题描述 投票:0回答:2

我对Node.js很陌生,我正在学习udemy上的Node.js课程,在多次重新运行集成测试时遇到listen EADDRINUSE: address already in use :::4000的麻烦。第一次成功,但随后我在下一行收到上述错误

const server = app.listen(port, () => {winston.info(`Listening on port ${port}`)});

我正在粘贴我的index.js和两个测试文件,如果有人可以指出我的信息,对我会有很大帮助。

Index.js

    const Joi = require("@hapi/joi");
    Joi.objectId = require("joi-objectid")(Joi);
    const winston = require("winston");
    const express = require("express");
    const app = express();

    require("./startup/logging")();
    require("./startup/config")();
    require("./startup/dbconnectivity")();
    require("./startup/routes")(app);
    const port = process.env.port || 4000;
    const server = app.listen(port, () => {winston.info(`Listening on port ${port}`)});
    // exporting server object to be used in integration tests.
    module.exports = server;


**Integration test file for Genre**

const request = require("supertest");
let server;
const {Genere} = require("../../models/genere");
const {User} = require("../../models/user");

describe("/api/genere", () => {
    beforeEach(() => {
        console.log("Before each Genre");
        server = require("../../index");
    });
    afterEach(async () => {
        console.log("After each Genre");
        await Genere.deleteMany({});
        await server.close();
    });

    describe("/GET", () => {
        it("should return list of generes", async() => {
            await Genere.insertMany([
                {name: "genre1"},
                {name: "genre2"},
                {name: "genre3"}
            ]);
            const res = await request(server).get("/api/geners");
            expect(res.status).toBe(200);
            console.log("response body is : " + res.body);
            expect(res.body.length).toBe(3);
            expect(res.body.map(g => g.name)).toContain("genre1");
        });
    });

    describe("/GET/:id", () => {
        it("should return genre with id", async() => {
            const genre = new Genere({name: "genre1"});
            await genre.save();
            const res = await request(server).get("/api/geners/"+ genre.id);
            expect(res.status).toBe(200);
            expect(res.body.name).toBe("genre1");
        });

        it("should return error with invalid id", async() => {
            const genre = new Genere({name: "genre1"});
            await genre.save();
            const res = await request(server).get("/api/geners/1");
            expect(res.status).toBe(404);
            expect(res.text).toMatch(/Invalid/);

        });
    });

    describe("/POST", () => {
        it("should return 401 if not authorized", async() => {
            const genere = new Genere({name: "genere1"});
            const res = await request(server).post("/api/geners").send(genere);
            expect(res.status).toBe(401);
        });

        it("should return 400 if the name is less than 4 chars", async() => {
            const res = await createRequestWithGenre({name: "g1"});
            expect(res.status).toBe(400);
        });

        it("should return 400 if the name is greater than 25 chars", async() => {
            const genreName = Array(26).fill("a").join("");
            const res = await createRequestWithGenre({name: genreName})
            expect(res.status).toBe(400);
        });

        it("should return 201 with gener object if proper object is sent", async() => {
            const res = await createRequestWithGenre({name: "genre1"})
            expect(res.status).toBe(201);
            expect(res.body).toHaveProperty("_id");
            expect(res.body).toHaveProperty("name", "genre1");

            const genre = await Genere.find({ name: "genre1"});
            expect(genre).not.toBe(null);
        });

        async function createRequestWithGenre(genre) {
            const token = new User().generateAuthToken();
            return await request(server)
            .post("/api/geners")
            .set("x-auth-token", token)
            .send(genre);
        }
    });
});

一旦我添加了另一个用于集成测试的文件,如下面的文件,我开始得到此文件代码后提到的错误。

const {User} = require("../../models/user");
 const {Genere} = require("../../models/genere");
 const request = require("supertest");
let token;

 describe("middleware", () => {

        beforeEach(() => {
            console.log("Before each Middleware");
            token = new User().generateAuthToken();
            server = require("../../index");
        });

        afterEach(async () => {
            console.log("After each Middleware");
            await Genere.deleteMany({});
            await server.close();
        });

        const exec = async() => {
            return await request(server)
            .post("/api/geners")
            .set("x-auth-token", token)
            .send({name: "gener1"});
        }

         it("should return 400 if invalid JWT token is sent", async() => {
            token = "invalid_token";
            const res = await exec();
            expect(res.status).toBe(400); 
            expect(res.text).toBe("Invalid auth token");
        });
  });

控制台错误

middleware
    ✕ should return 400 if invalid JWT token is sent (510ms)

  ● middleware › should return 400 if invalid JWT token is sent

    listen EADDRINUSE: address already in use :::4000

      10 | require("./startup/routes")(app);
      11 | const port = process.env.port || 4000;
    > 12 | const server = app.listen(port, () => {winston.info(`Listening on port ${port}`)});
         |                    ^
      13 | // exporting server object to be used in integration tests.
      14 | module.exports = server;

      at Function.listen (node_modules/express/lib/application.js:618:24)
      at Object.<anonymous> (index.js:12:20)
      at Object.beforeEach (tests/integration/middleware.test.js:11:22)

如果有人可以帮助我为什么它多次运行失败,那么对我来说为什么理解每次我们为什么需要打开和关闭服务器对象将非常有帮助。

node.js express jestjs integration-testing supertest
2个回答
0
投票
如官方documentation中所述,而不是beforeEach中所述,可以使用globalSetup在运行所有测试套件之前先初始化服务器,然后再停止服务器:]

// setup.js module.exports = async () => { // ... // Set reference to your node server in order to close it during teardown. global.__MY_NODE_SERVER__ = require("../../index"); }; // teardown.js module.exports = async function() { await global.__MY_NODE_SERVER__.stop(); }; // in your jest-config you'd set the path to these files: module.exports = { globalSetup: "<rootDir>/setup.js", globalTeardown: "<rootDir>/teardown.js", };

或者,您可以使用--runInBand optionbeforeAll而不是beforeEach来运行测试,以确保在每次测试之前仅创建一个服务器,但是我建议使用第一个选项。 >

如果您可以在不调用app的情况下导入app的实例,则[Supertest可以管理快递/ koa .listen()本身的设置/删除。
这涉及到一些不同的代码结构,因此app成为一个模块。

// app.js module const app = require('express')() require("./startup/logging")() ... module.exports = app

然后服务器的入口点执行listen()

// server.js entrypoint
const app = require('./app')
const port = process.env.port || 4000;
app.listen(port, () => {winston.info(`Listening on port ${port}`)});

当超级测试为app启动服务器时,它will listen on a random unused port没有冲突。

// test
const request = require('supertest')
const app = require('./app')
request(app).get('/whatever')

超级测试服务器实例也可以重复用于多个测试

// reuse test
const supertest = require('supertest')
const app = require('./app')

describe('steps', () => {
  const request = supertest(app)
  it('should step1', async() => {
    return request.get('/step1')
  })
  it('should step2', async() => {
    return request.get('/step2')
  })
})

0
投票
© www.soinside.com 2019 - 2024. All rights reserved.