使用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实例,避免影响真实数据,同时提升测试速度:
- 创建
.env.test文件,配置测试环境的数据库和Redis地址 - 启动测试时指定环境:
NODE_ENV=test npm test
内容的提问来源于stack exchange,提问作者user19263124
相关产品推荐
相关产品推荐

