使用Mocha时Promise使用错误:回调与Promise冲突及超时问题
执行测试命令 mocha --reporter spec --recursive --timeout 60000 时,遇到两类错误:
错误信息
- 错误1:
Error: Resolution method is overspecified. Specify a callback or return a Promise; not both.
- 错误2:
Error: Timeout of 60000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (C:\Users\GB\Documents\projects\requireurl\concurrency\test\test_demos_cluster.js)
at listOnTimeout (node:internal/timers:564:17)
原测试代码
const expect = require('chai').expect; describe('test-.mjs::concurrency.js: Test Suite for concurrency.js Files', function () { var result before(async function (done) { function testPromise() { return new Promise(function (resolve, reject) { resolve({ msg: "testing" }); }) } result = await testPromise(); // return a promise with result done() }); describe('test-.js::concurrency.js: [Test A] Test Suite for concurrency.js in main repo directory', function () { it('[Test A] Test for ', function (done) { // expect(100).to.equal(100); expect(result.msg).to.equal("testing"); done(); }); }); });
问题分析
第一个错误是核心原因:Mocha不允许在同一个异步钩子或测试中同时使用async/await(会自动返回Promise)和done回调。当你同时用这两种方式时,Mocha无法确定应该以哪种方式判断异步操作完成,就会抛出"Resolution method is overspecified"的错误。
第二个超时错误是第一个错误引发的连锁问题:Mocha处理冲突的异步声明时出现异常,导致后续测试流程卡住,最终触发超时。
修复方案
方案1:利用async/await自动处理Promise(推荐)
直接去掉before钩子的done参数,async函数会自动返回Promise,Mocha会等待这个Promise完成后再执行后续测试:
const expect = require('chai').expect; describe('test-.mjs::concurrency.js: Test Suite for concurrency.js Files', function () { var result before(async function () { // 去掉done参数 function testPromise() { return new Promise(function (resolve, reject) { resolve({ msg: "testing" }); }) } result = await testPromise(); // 不再需要调用done() }); describe('test-.js::concurrency.js: [Test A] Test Suite for concurrency.js in main repo directory', function () { it('[Test A] Test for ', function () { // 这里没有异步操作,也不需要done expect(result.msg).to.equal("testing"); }); }); });
方案2:使用done回调处理Promise
如果习惯用done回调,可以去掉async关键字,在Promise的then中调用done,同时要捕获错误传给done(避免错误时测试超时):
const expect = require('chai').expect; describe('test-.mjs::concurrency.js: Test Suite for concurrency.js Files', function () { var result before(function (done) { // 去掉async function testPromise() { return new Promise(function (resolve, reject) { resolve({ msg: "testing" }); }) } testPromise().then(res => { result = res; done(); // 异步完成后调用done }).catch(err => done(err)); // 捕获错误并传给done,避免超时 }); describe('test-.js::concurrency.js: [Test A] Test Suite for concurrency.js in main repo directory', function () { it('[Test A] Test for ', function () { // 无异步操作,去掉done expect(result.msg).to.equal("testing"); }); }); });
额外优化
后面的it测试中没有异步操作,完全不需要done回调,直接编写同步断言即可,这样代码更简洁。
内容的提问来源于stack exchange,提问作者Gary

