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

使用Jest和Supertest测试MERN应用持续失败的问题求助

测试MERN栈应用时的Jest超时与异步残留问题

问题说明

我正在使用Jest和Supertest测试一个MERN栈应用,该应用用Redis存储用户refresh token,同时通过node-cron执行定时后台任务。测试持续失败,遇到两类错误:

  • 测试超时:提示Exceeded timeout of 5000 ms for a test
  • Jest无法正常退出:测试完成1秒后仍提示存在未停止的异步操作

已尝试以下方法但未解决:

  • 执行npm test --detectOpenHandles排查
  • 添加afterAll(async () => { await app.close(); })
  • 使用done()回调
  • 增加测试超时时间

相关代码片段

测试代码

const request = require('supertest')
const app = require('../app')

describe('Test POST /register', ()=>{

  test('It should respond with 200 success', async ()=>{
    const response = await request(app).post('/register');
    expect(response.statusCode).toBe(200);
   },);

   test('It should catch password must be present', ()=>{});

})

路由配置

router.post('/register', register);

app.js

app.use("/api/v1/auth", authRoute);
app.use("/api/v1/users",userRoute);

app.get('*', (req, res)=>{
  res.sendFile(path.join(__dirname, 'build', 'index.html'));
})

app.use(checkDB);

module.exports = app;

server.js

const http = require('http');
require('dotenv').config();//declaring the envy file

const app = require('./app');
//mongoDb
const connectDB = require('./db/connect');

const port = process.env.PORT || 5000;

const server = http.createServer(app);

const start = async () => {
 try {
 await connectDB(process.env.MONGO_URL)
 server.listen(port, () =>
  console.log(`Server is listening on port ${port}...`)
  );
 } catch (error) {
console.log(error);
 }
};

start();

解决方案

1. 分离服务器启动逻辑,控制资源生命周期

当前server.js自动启动服务并连接数据库,导致测试时残留未关闭的连接和服务。修改为可手动控制启动/关闭:

const http = require('http');
require('dotenv').config();

const app = require('./app');
const connectDB = require('./db/connect');

const port = process.env.PORT || 5000;

let server;

const start = async () => {
  try {
    await connectDB(process.env.MONGO_URL);
    server = http.createServer(app).listen(port, () =>
      console.log(`Server is listening on port ${port}...`)
    );
  } catch (error) {
    console.log(error);
  }
};

// 暴露关闭服务器的方法
const closeServer = async () => {
  if (server) {
    await new Promise((resolve) => server.close(resolve));
  }
};

module.exports = { start, closeServer };

在测试文件中管理服务生命周期:

const request = require('supertest')
const app = require('../app')
const { start, closeServer } = require('../server');

describe('Test POST /register', ()=>{
  // 测试前启动服务
  beforeAll(async () => {
    await start();
  });

  // 测试后清理所有资源
  afterAll(async () => {
    await closeServer();
    // 关闭MongoDB连接
    const mongoose = require('mongoose');
    await mongoose.connection.close();
    // 关闭Redis连接(替换为你的Redis客户端实例路径)
    const redisClient = require('../utils/redisClient');
    await redisClient.quit();
    // 停止node-cron定时任务(替换为你的任务实例)
    const cleanupJob = require('../jobs/cleanupJob');
    cleanupJob.stop();
  });

  test('It should respond with 200 success', async ()=>{
    // 传入接口必填参数,避免接口等待输入导致超时
    const response = await request(app).post('/register').send({
      email: 'test@example.com',
      password: 'test123456',
      username: 'testuser'
    });
    expect(response.statusCode).toBe(200);
  }, 10000); // 可选:给该测试单独设置更长超时

  test('It should catch password must be present', async ()=>{
    const response = await request(app).post('/register').send({
      email: 'test@example.com',
      username: 'testuser'
    });
    expect(response.statusCode).toBe(400);
    expect(response.body.message).toBe('password must be present');
  });
})

2. 修复测试用例逻辑

  • 第一个测试用例未传入任何参数,接口可能因缺少必填字段挂起,导致超时,必须补充模拟参数
  • 第二个测试用例为空,需补充完整的参数校验逻辑

3. 彻底清理异步资源

  • Redis连接必须调用quit()而非disconnect(),确保待处理命令执行完毕后再关闭
  • node-cron定时任务需在afterAll中调用stop()方法停止
  • MongoDB连接需调用connection.close()关闭

4. 使用测试专用资源

测试时切换到单独的测试数据库和Redis实例,避免影响真实数据,同时提升测试速度:

  1. 创建.env.test文件,配置测试环境的数据库和Redis地址
  2. 启动测试时指定环境:NODE_ENV=test npm test

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 04:20:32