我的node.js摩卡套件中的典型测试如下所示:
it("; client should do something", function(done) { var doneFn = function(args) { // run a bunch of asserts on args client.events.removeListener(client.events.someEvent, userMuteFn); done(); } client.events.on(someEvent, doneFn); client.triggerEvent(); });
这里的问题是,如果client.triggerEvent()操作不正确,或者服务器中断并且从不调用someEvent,done()则将永远不会被调用。这给以前从未与测试人员一起工作过的人留下了模棱两可的错误,例如:
client.triggerEvent()
someEvent
done()
Error: timeout of 10500ms exceeded. Ensure the done() callback is being called in this test.
我的问题是,是否有一种方法可以重写这些测试,无论是与mocha一起使用还是与另一个lib一起使用,都可以使异步工作更容易遵循。我希望能够输出以下内容:
the callback doneFn() was never invoked after clients.event.on(...) was invoked
或类似的东西。
我不确定是否使用诸如promises之类的方法会有所帮助。对于异步/回调类型代码,更有意义的错误消息将是一件好事。如果这意味着从回调/异步转移到另一个工作流,那么我也同意。
有什么解决方案?
当您收到超时错误而不是更精确的错误时, 要做的第一件事是检查您的代码没有吞噬异常,也没有吞噬承诺拒绝。 Mocha旨在检测您的测试中的这些。除非您执行不寻常的操作(例如在自己的VM中运行测试代码或操纵域),否则Mocha会检测到此类故障,但是如果您的代码吞噬了这些故障,Mocha将无法执行任何操作。
话虽这么说,Mocha无法告诉您done未调用,因为您的实现存在逻辑错误,导致无法调用回调。
done
在sinon测试失败后执行后期验验可以采取以下措施。让我强调,这是 概念 的 证明 。如果我想持续使用它,那么我将开发一个适当的库。
sinon
var sinon = require("sinon"); var assert = require("assert"); // MyEmitter is just some code to test, created for the purpose of // illustration. var MyEmitter = require("./MyEmitter"); var emitter = new MyEmitter(); var postMortem; beforeEach(function () { postMortem = { calledOnce: [] }; }); afterEach(function () { // We perform the post mortem only if the test failed to run properly. if (this.currentTest.state === "failed") { postMortem.calledOnce.forEach(function (fn) { if (!fn.calledOnce) { // I'm not raising an exception here because it would cause further // tests to be skipped by Mocha. Raising an exception in a hook is // interpreted as "the test suite is broken" rather than "a test // failed". console.log("was not called once"); } }); } }); it("client should do something", function(done) { var doneFn = function(args) { // If you change this to false Mocha will give you a useful error. This is // *not* due to the use of sinon. It is wholly due to the fact that // `MyEmitter` does not swallow exceptions. assert(true); done(); }; // We create and register our spy so that we can conduct a post mortem if the // test fails. var spy = sinon.spy(doneFn); postMortem.calledOnce.push(spy); emitter.on("foo", spy); emitter.triggerEvent("foo"); });
这是以下代码MyEmitter.js:
MyEmitter.js
var EventEmitter = require("events"); function MyEmitter() { EventEmitter.call(this); } MyEmitter.prototype = Object.create(EventEmitter.prototype); MyEmitter.prototype.constructor = MyEmitter; MyEmitter.prototype.triggerEvent = function (event) { setTimeout(this.emit.bind(this, event), 1000); }; module.exports = MyEmitter;