You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

设置Jest为runInBand后仍遇Supertest端口占用问题

Node单元测试EADDRINUSE错误解决方案

问题背景

使用Supertest+Jest测试Node路由时,反复遇到端口占用错误:

code: 'EADDRINUSE',
  errno: -4091,
  syscall: 'listen',
  address: '::',
  port: 5000

已尝试配置:

  • package.json测试命令:
    "test": "jest --verbose --watchAll --runInBand --detectOpenHandles --forceExit --coverage"
    
  • 设置maxWorkers=1

相关代码片段:

index.js

app.use("/api/v1", clinicRoutes);

let PORT;

process.env.STATUS === "production"
  ? (PORT = process.env.PROD_PORT)
  : (PORT = process.env.DEV_PORT);

app.listen(PORT, () => {
  logger.info(
    `app is running in ${process.env.STATUS} mode, listening on port ${PORT}`
  );
});

index.test.js

const request = require("supertest");
const app = require("../index");

describe("Test the clinic search API", () => {
  test("It should respond with a list of clinics", async () => {
    const response = await request(app)
      .post("/api/v1/search")
      .send({
        name: "Scratch",
        state: "California",
        availableFrom: "01:00",
        availableTo: "23:00",
      })
      .set("Accept", "application/json");

    expect(response.statusCode).toBe(200);
    expect(response.body).toHaveProperty("data");
    expect(Array.isArray(response.body.data)).toBe(true);
    expect(response.body.data[0]).toHaveProperty("name");
    expect(response.body.data[0]).toHaveProperty("state");
    expect(response.body.data[0]).toHaveProperty("availability");
    expect(response.body.data[0].availability).toHaveProperty("from");
    expect(response.body.data[0].availability).toHaveProperty("to");
  });
});

解决方法

1. 分离App实例与端口监听逻辑(核心方案)

问题根源:每次运行测试时,require("../index")都会执行app.listen(),导致端口被重复占用。

修改index.js,仅在非测试环境启动监听:

// index.js
const express = require('express');
const app = express();

app.use("/api/v1", clinicRoutes);

// 仅在非测试环境启动服务
if (process.env.NODE_ENV !== 'test') {
  let PORT = process.env.STATUS === "production" 
    ? process.env.PROD_PORT 
    : process.env.DEV_PORT;

  app.listen(PORT, () => {
    logger.info(
      `app is running in ${process.env.STATUS} mode, listening on port ${PORT}`
    );
  });
}

module.exports = app;

Supertest无需实际启动端口即可直接测试Express实例,这样就不会触发端口占用问题。

2. 为测试环境分配独立端口(可选)

如果测试场景需要启动完整服务,可单独指定测试端口:

  1. 创建jest.setup.js:
process.env.NODE_ENV = 'test';
process.env.DEV_PORT = '5001'; // 使用与开发/生产环境不同的端口
  1. 在jest.config.js中引入:
module.exports = {
  testEnvironment: 'node',
  setupFiles: ['./jest.setup.js']
};

3. 测试结束后手动关闭服务器(兜底)

若上述方法无效,可在测试生命周期中手动管理服务器:

// index.test.js
const request = require("supertest");
const app = require("../index");
let server;

beforeAll(() => {
  // 手动启动服务器并保存引用
  server = app.listen(5001);
});

afterAll((done) => {
  // 测试完成后关闭服务器,释放端口
  server.close(done);
});

describe("Test the clinic search API", () => {
  // 原有测试用例保持不变
});

4. 清理占用端口的外部进程

检查端口5000是否被其他进程占用:

  • macOS/Linux:
    lsof -i :5000 # 查看占用进程
    kill -9 <PID> # 替换为实际进程ID,强制终止
    
  • Windows:
    netstat -ano | findstr :5000 # 查看占用进程ID
    taskkill /F /PID <PID> # 强制终止进程
    

内容的提问来源于stack exchange,提问作者Olaiwola Ayoola

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 23:47:27