使用Mocha与Chai测试Node.js API路由时遭遇超时故障求助
解决Mocha测试API路由超时问题及代码优化建议
你遇到的测试超时问题,大概率是异步操作耗时超出Mocha默认阈值,再加上路由和测试代码里的几个小逻辑漏洞导致的,我来帮你一步步梳理解决:
一、先搞定测试超时问题
Mocha默认给异步测试的超时时间只有2000ms,而文件上传+数据库存储的操作很容易超过这个时长。你可以给测试用例单独设置更长的超时时间:
describe('POST /content/image_upload', () => { // 给整个测试块设置10秒超时,根据实际情况调整 this.timeout(10000); it('should return status 200', async function() { // 如果用箭头函数拿不到this,也可以在这里单独设置 // this.timeout(10000); // ... 你的测试代码 }) })
二、修复路由处理器的逻辑漏洞
你的路由代码有几个可能导致响应卡住或重复发送的问题:
1. 条件判断后未加return,可能触发多次响应
比如当检测到参数缺失时,你发送了400响应,但代码会继续往下执行,后续可能又发送一次响应,导致Node.js报错,测试也会因为响应异常而超时。修改成:
if(files == undefined || originalUrls == undefined){ return res.status(400).send({status:'failed', message:"input field cannot be undefined"}) }
同理,最后else分支也加上return:
}else{ return res.status(400).send({status:'failed', message:"input error"}) }
2. 兼容单个文件/单个URL的情况
当用户只上传一个文件时,req.files['files[]']可能是单个对象而非数组;同理originalUrls如果是单个字符串,originalUrls.length会是字符串长度而非数组长度,导致判断错误。先统一转成数组:
// 统一处理成数组格式 files = Array.isArray(files) ? files : [files]; originalUrls = Array.isArray(originalUrls) ? originalUrls : [originalUrls];
3. 校验文件和URL数量匹配
如果上传的文件数和URL数不一致,会导致originalUrls[index]为undefined,建议增加校验:
if(files.length !== originalUrls.length){ return res.status(400).send({status:'failed', message:"number of files must match number of original urls"}) }
三、优化测试代码
1. 无需手动设置Content-Type
chai-http的attach和field会自动生成正确的multipart/form-data请求头,手动设置反而可能出错,删掉这一行:
.set('Content-Type', 'application/form-data')
2. 修正attach的文件名参数
attach的第三个参数应该是文件的原始名称,比如:
.attach('files[]', fs.readFileSync(path.join(__dirname,'./asset/listEvent.png')), 'listEvent.png')
完整修改后的路由处理器示例
exports.imageUpload = (req, res, next) => { Upload(req,res, async () => { let files = req.files['files[]']; let originalUrls = req.body.orgUrl; // 校验必填参数 if(files == undefined || originalUrls == undefined){ return res.status(400).send({status:'failed', message:"input field cannot be undefined"}) } // 统一转为数组格式 files = Array.isArray(files) ? files : [files]; originalUrls = Array.isArray(originalUrls) ? originalUrls : [originalUrls]; // 校验数量匹配 if(files.length !== originalUrls.length){ return res.status(400).send({status:'failed', message:"number of files must match number of original urls"}) } if(files.length > 0){ const promises = files.map(async(file,index)=>{ const imageDoc = new ImageModel({ croppedImageUrl : file.path, originalImageUrl: originalUrls[index], status: 'New' }); return await imageDoc.save(); }); try{ const response = await Promise.all(promises); return res.status(200).send({status: 'success', res: response}); }catch(error){ return res.status(500).send({status:"failed", error: error.message}) // 返回更友好的错误信息 } }else{ return res.status(400).send({status:'failed', message:"input error"}) } }) }
完整修改后的测试代码示例
describe('POST /content/image_upload', () => { this.timeout(10000); it('should return status 200', async function() { const response = await chai.request(app) .post('/content/image_upload') .attach('files[]', fs.readFileSync(path.join(__dirname,'./asset/listEvent.png')), 'listEvent.png') .field('orgUrl', 'https://images.unsplash.com/photo-1556830805-7cec0906aee6?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1534&q=80') expect(response.error).to.be.false; expect(response.status).to.be.equal(200); expect(response.body.status).to.be.equal('success'); response.body.res.forEach(item => { expect(item).to.have.property('croppedImageUrl'); expect(item).to.have.property('originalImageUrl'); expect(item).to.have.property('status'); expect(item).to.have.property('_id'); }) }) })
按照这些修改,应该能解决超时问题,同时修复路由里的逻辑漏洞,让测试顺利通过。
内容的提问来源于stack exchange,提问作者murshi
相关产品推荐
相关产品推荐

