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

Jest+Supertest测试Express时调用Auth0异步函数出现open handles无法正常退出

问题中两个TLSWRAP类型的open handle分别对应两个HTTPS请求的连接残留:一个是getToken调用Auth0接口的HTTPS连接,另一个是supertest调用本地Express接口的HTTP/HTTPS连接,均由默认开启的TCP连接复用(keep-alive)未主动释放导致。

解决方案

1. 修复axios连接复用问题(优先解决Auth0请求导致的句柄)

getToken函数中axios默认开启了HTTP keep-alive,会复用HTTPS连接导致句柄残留,修改getToken的请求配置,显式禁用keep-alive:

import http from 'http'
import https from 'https'
import axios from 'axios'

var options = {
    url: '你的Auth0接口地址',
    form: {
      // 原有表单配置不变
    },
    json: true
  };
  
  const getToken = async () => {
    try {
      const tokenRequest = await axios.post(options.url, options.form, {
        // 新增配置,禁用连接复用
        httpAgent: new http.Agent({ keepAlive: false }),
        httpsAgent: new https.Agent({ keepAlive: false }),
      })
      return tokenRequest.data.access_token
    } catch (err){
      return err
    }
  } 

export default getToken;

2. 修复mongoose连接的await遗漏

你当前beforeAll里的mongoose.connect没有加await,属于异步操作未等待完成就执行后续逻辑,可能导致数据库连接句柄残留,修改测试文件的beforeAll逻辑:

beforeAll(async() => {
  try {
    // 新增await等待数据库连接完成
    await mongoose.connect(config.MONGODB_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
      useCreateIndex: true,
    });
    db = mongoose.connection;
    db.on("error", console.error.bind(console, "Console Error:"));
    db.once("open", () =>
      console.log(`App connected to "${db.name}" database`)
    );
    authToken = await getToken()
  } catch (err) {
    return err
  }
});

3. 显式关闭Express服务(解决supertest请求的句柄残留)

第二个TLSWRAP是supertest调用本地接口时启动的HTTP服务没有主动关闭,修改测试文件,显式管理Express服务的生命周期:

import app from "../app";
import mongoose from "mongoose";
import supertest from "supertest";
import { getToken } from "../helpers";
import dotenv from "dotenv";
import * as config from "../config";
import http from 'http'

dotenv.config();

const api = supertest(app);

let authToken: any;
let db: any;
// 新增server变量存储Express服务实例
let server: http.Server;

beforeAll(async() => {
  try {
    await mongoose.connect(config.MONGODB_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
      useCreateIndex: true,
    });
    db = mongoose.connection;
    db.on("error", console.error.bind(console, "Console Error:"));
    db.once("open", () =>
      console.log(`App connected to "${db.name}" database`)
    );
    authToken = await getToken()
    // 新增:启动Express服务,随机端口避免冲突
    server = app.listen(0)
  } catch (err) {
    return err
  }
});

describe("GET /interview/:idCandidate", () => {
  test("With auth0 and read permissions", async () => {
       await api
        .get("/interview/1")
        .set("Authorization", "Bearer " + authToken)
        .expect(200)
  });
});

afterAll(async () => {
  try {
    await db.close();
    // 新增:关闭Express服务,等待关闭完成
    await new Promise(resolve => server.close(resolve))
  } catch (err) {
    return err;
  }
});

4. 兜底方案:强制Jest退出

如果以上方案都不能解决,可在jest的启动命令中添加--forceExit参数,强制Jest在所有测试用例执行完成后直接退出,无需等待所有句柄释放:

jest --detectOpenHandles --forceExit

该方案适合端到端测试场景,不会影响测试结果的正确性。


内容的提问来源于stack exchange,提问作者Mauro Consolani

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 14:18:05