Mocha+Chai+Sinon测试异常:Promise中called属性始终为true的原因及解决
called属性也总是返回true? 我来帮你拆解这个问题——你遇到的这个“无论什么函数,called都返回true”的假象,其实大概率是Promise错误处理不当导致的,再加上可能的Spy引用问题,咱们一步步说:
核心原因:Promise的catch块没有传递错误给Mocha
看你的测试代码里的catch部分:
.catch(err => { done(); });
如果你的断言(比如expect(instance.view.startCountDown.called).to.be.ok)失败,会抛出一个错误,这个错误会被Promise的catch捕获。但你在这里直接调用done(),没有把错误传递给它,Mocha就会认为测试“成功完成”,而不会知道断言已经失败了!
换句话说,不是called属性真的返回true,而是断言失败的错误被你吞掉了,导致你误以为测试通过了。
另外还有一个可能的小坑:如果你断言的不是Sinon Spy对象的called属性,而是普通函数甚至未定义的属性,JavaScript的特性会让结果超出预期:
- 如果你访问一个普通函数的
called属性(不是Sinon Spy),这个属性不存在,会返回undefined,而Chai的to.be.ok会把undefined判定为falsy,断言应该失败——但因为你吞掉了错误,所以看起来像是通过了。 - 如果不小心创建了全局的
called变量(比如漏写了let/const),那访问任何对象的called属性都可能拿到这个全局值,导致误判。
解决方案
1. 修复Promise的错误处理
在catch块里把错误传递给done(),让Mocha捕获到断言失败:
result.then(res => { expect(instance.view.startCountDown.called).to.be.ok; done(); }).catch(err => { done(err); // 关键:把错误传给done,Mocha才会标记测试失败 });
或者更推荐用async/await简化代码,避免手动处理done,错误会自动被Mocha捕获:
it('if get server response success, should start countdown', async function () { // given sandbox.stub(model, 'getVerifyCode').resolves({}); sandbox.spy(view, 'startCountDown'); // when await instance.onClickVerify('123456'); // then expect(instance.view.startCountDown.called).to.be.ok; });
2. 确保Spy的引用正确
确认你Spy的view和instance.view是同一个对象引用。如果instance.view是测试前就创建的实例,而你Spy的是另一个view对象,那你的Spy根本不会生效。可以在测试里加一行验证:
console.log(instance.view === view); // 应该输出true
3. 排查全局变量污染
检查你的代码里有没有不小心写了called = true这种没有声明的变量,这会创建全局的called变量,导致意外的truthy值。
验证方法
先把catch块改成传递错误给done(),再运行测试——这时候如果startCountDown没被调用,或者你断言未定义的函数,测试会正确失败,而不是之前的“假通过”。
内容的提问来源于stack exchange,提问作者kHRYSTAL

