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

Jest单元测试进程未优雅退出问题求助(已用临时方案)

解决Jest单元测试中进程无法优雅退出的问题

问题现象

运行Jest单元测试时收到警告:

A worker process has failed to exit gracefully and has been force exited. This is likely caused by tests leaking due to improper teardown. Try running with --detectOpenHandles to find leaks. Active timers can also cause this, ensure that .unref() was called on them.

尝试添加--detectOpenHandles参数时出现错误,目前临时使用package.json中的命令:

"test:ci": "jest --runInBand --forceExit"

但仍会收到提示使用--detectOpenHandles的警告,且这个方案只是强制退出,并非根本解决办法。

已执行的清理操作

已经在测试中添加了数据库连接关闭逻辑:

afterAll(async () => {
  if (mongo) {
    await mongo.stop();
  }
  mongoose.connection.close();
});

单元测试代码

import request from 'supertest';
import { app } from '../../app';

it('fails when a email that does not exist is supplied', async () => {
  await request(app)
    .post('/api/users/signin')
    .send({
      email: 'test@test.com',
      password: 'password',
    })
    .expect(400);
});

it('fails when an incorrect password is supplied', async () => {
  await request(app)
    .post('/api/users/signup')
    .send({
      email: 'test@test.com',
      password: 'password',
    })
    .expect(201);

  await request(app)
    .post('/api/users/signin')
    .send({
      email: 'test@test.com',
      password: 'aslkdfjalskdfj',
    })
    .expect(400);
});

it('responds with a cookie when given valid credentials', async () => {
  await request(app)
    .post('/api/users/signup')
    .send({
      email: 'test@test.com',
      password: 'password',
    })
    .expect(201);

  const response = await request(app)
    .post('/api/users/signin')
    .send({
      email: 'test@test.com',
      password: 'password',
    })
    .expect(200);

  expect(response.get('Set-Cookie')).toBeDefined();
});

排查与解决建议

1. 修复数据库连接关闭的异步逻辑

mongoose.connection.close()是异步操作,当前代码没有加await,会导致Jest在连接未完全关闭时尝试退出。修改afterAll:

afterAll(async () => {
  if (mongo) {
    await mongo.stop();
  }
  await mongoose.connection.close(); // 新增await等待连接关闭完成
});

2. 关闭Express服务器实例

测试中使用supertest请求的Express应用如果未关闭,会持有HTTP服务器句柄导致泄漏。需要在应用文件中导出服务器引用,然后在测试中关闭:

  • 比如app.ts中修改导出:
import express from 'express';
const app = express();
// ... 其他路由和配置
const server = app.listen(process.env.PORT || 3000);
export { app, server };
  • 在测试文件中更新afterAll:
import { app, server } from '../../app';

afterAll(async () => {
  // 先清理数据库
  if (mongo) {
    await mongo.stop();
  }
  await mongoose.connection.close();
  // 关闭服务器,等待回调确认完成
  await new Promise(resolve => server.close(resolve));
});

3. 清除测试间的数据库状态污染

当前测试中,前一个测试创建的用户会残留到后续测试,不仅可能导致测试不稳定,也可能因数据库连接未正确重置引发泄漏。建议在每个测试前清空数据:

import { User } from '../../models/user'; // 导入你的用户模型

beforeEach(async () => {
  await User.deleteMany({}); // 清空用户集合
});

4. 排查定时器泄漏

检查业务代码或测试代码中是否有未清理的setTimeout/setInterval,这类定时器会阻止Jest优雅退出。处理方式:

  • 对非必要的定时器调用unref(),允许进程在定时器未完成时退出:
const timer = setTimeout(() => {}, 1000);
timer.unref();
  • 或者在测试完成后主动清除:
afterEach(() => {
  // 清除所有定时器
  jest.clearAllTimers();
});

5. 定位具体泄漏点

如果--detectOpenHandles全局运行出错,可以尝试单独运行某个测试文件来定位:

jest --detectOpenHandles tests/users/signin.test.ts

逐步缩小范围,找到引发泄漏的具体测试或代码逻辑。

内容的提问来源于stack exchange,提问作者Iynga Iyngaran Iyathurai

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 19:09:28