Mocha框架中循环生成it()测试用例但回调代码未执行的问题
解决Mocha循环中
it()测试代码不执行的问题 这问题我之前碰过好多次,本质是JavaScript的闭包陷阱和Mocha的测试运行机制冲突导致的。
问题根源
Mocha的工作逻辑是先完成所有it()测试用例的注册,等全部注册完才会逐个执行测试代码。你用for...in循环创建it()的时候,所有回调函数共享同一个inputFilename变量的引用——等Mocha真正开始跑测试时,循环早就跑完了,所有回调里的变量都指向最后一次循环的值,甚至会因为作用域的问题,导致测试根本没被正确触发执行。
解决方案
这里有两个靠谱的解决办法,按需选就行:
方案1:用ES6 let声明变量(推荐)
把循环里的var改成let,let会在每次循环迭代时创建一个独立的变量绑定,每个it()的回调都会捕获当前迭代的那个变量,完美避开闭包陷阱:
var unitTests = JSON.parse(fs.readFileSync('./test/unitTests.json', 'utf8')); for (test in unitTests.unitTests) { let inputFilename = unitTests.unitTests[test].input; // 换成let console.log(inputFilename); it(`处理文件: ${inputFilename}`, function(done) { // 这里可以正常使用当前迭代的inputFilename // 你的测试逻辑,比如读取文件、断言验证等 done(); // 异步测试一定要记得调用done()结束! }); }
方案2:立即执行函数(IIFE)绑定变量
如果你的运行环境不支持ES6,就用立即执行函数把每次迭代的变量封装成独立作用域:
var unitTests = JSON.parse(fs.readFileSync('./test/unitTests.json', 'utf8')); for (test in unitTests.unitTests) { var inputFilename = unitTests.unitTests[test].input; console.log(inputFilename); // 用IIFE捕获当前的inputFilename副本 (function(filename) { it(`处理文件: ${filename}`, function(done) { // 这里用filename就是当前迭代的变量了 done(); }); })(inputFilename); }
额外优化建议
- 给每个
it()的测试名称加上具体文件名,这样测试结果里能清晰看到每个用例对应哪个文件,排查问题更方便。 - 尽量用
Object.keys()遍历对象属性,比for...in更安全,不会误遍历到原型链上的属性:
Object.keys(unitTests.unitTests).forEach(function(testKey) { let inputFilename = unitTests.unitTests[testKey].input; console.log(inputFilename); it(`处理文件: ${inputFilename}`, function(done) { // 测试逻辑 done(); }); });
- 确认异步测试里正确调用了
done(),如果漏写的话,Mocha会认为测试超时失败,看起来也像没执行一样。
内容的提问来源于stack exchange,提问作者Paul Cezanne
相关产品推荐
相关产品推荐

