【问题标题】:node.js how to get better error messages for async tests using mochanode.js 如何使用 mocha 为异步测试获取更好的错误消息
【发布时间】:2016-10-31 05:46:17
【问题描述】:

我的 node.js mocha 套件中的典型测试如下所示:

 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() 将永远不会被调用。这会给之前没有使用过测试套件的人留下一个模棱两可的错误,例如:

Error: timeout of 10500ms exceeded. Ensure the done() callback is being called in this test.

我的问题是,有没有一种方法可以重写这些测试,无论是使用 mocha 还是添加另一个库,都可以使这样的异步工作更容易遵循。我希望能够输出如下内容:

the callback doneFn() was never invoked after clients.event.on(...) was invoked

或者类似的东西。

我不确定使用诸如承诺之类的东西是否会对此有所帮助。对于异步/回调类型代码更有意义的错误消息将是一件好事。如果这意味着从回调/异步转移到另一个工作流,我也可以。

有哪些解决方案?

【问题讨论】:

  • 可以澄清变量client 包含的内容。它是一个对象实例,还是一个使用child_process.fork() 调用的模块,或者别的什么?那方面的错误是如何管理的?

标签: javascript node.js asynchronous mocha.js


【解决方案1】:

除了someEvent,您还应该收听uncaughtException 事件。这样您就可以捕捉到客户端发生的错误,并且这些错误会显示在您的测试报告中。

 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();
    }

    var failedFn = function(err) {
      client.events.removeListener('uncaughtException', failedFn);
      // propagate client error
      done(err);
    }
    client.events.on(someEvent, doneFn);

    client.events.on('uncaughtException', failedFn);

    client.triggerEvent();
});

附:如果client 是子进程,您还应该监听exit 事件,该事件将在子进程死亡时触发。

【讨论】:

  • Mocha installs 一个 uncaughtException 全局处理程序。没有理由用特殊代码捕获uncaughtException
  • @Louis as OP 在他的问题中描述了“服务器”和“客户端”组件,摩卡可能在一端运行,而无法访问另一端发生的异常(它自己运行过程)。
  • 我明白了。据我所知,人们可能默认使用的服务器设施(例如express)不会生成客户端可以收听的uncaughtException 事件。接下来的挑战是如何使用将生成这些事件的安全网来检测服务器代码。
【解决方案2】:

当您收到超时错误而不是更精确的错误时,首先要做的是检查您的代码是否不会吞下异常,并且不会吞下承诺拒绝。 Mocha 是旨在在您的测试中检测到这些。除非你做一些不寻常的事情,比如在你自己的 VM 中运行测试代码或操纵 domains,否则 Mocha 会检测到此类故障,但如果你的代码吞噬了它们,那么 Mocha 就无能为力了。

话虽如此,Mocha 无法告诉您 done 未被调用,因为您的实现有一个逻辑错误导致它无法调用回调。

下面是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的代码:

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;

【讨论】:

  • 是什么给this.currentTest增加了状态?
  • 有没有机会在 mocha 文档中链接到该链接?我碰巧没看到。
  • 我认为另一个问题是,如果不调用 done()currentTest.state 永远不会设置为 failed
  • 我不知道文档中提到它的任何部分。这是 Mocha 文档中缺少的一部分。我多年来在 SO 上回答了 Mocha 问题并阅读了 github 上的错误报告,Mocha 开发人员在钩子中访问 this.currentTest 时表现良好。 (例如this report。如果我们根本不应该使用this.currentTest,它将被立即关闭为“不是问题”。)
  • 关于您对currentTest.state 未设置为failed 的评论(如果未调用done()),我无法在此处重现。如果我将console.log("state", this.currentTest.state); 添加到我的afterEach 钩子并弄乱我在答案中显示的代码以便不调用done,我每次都会得到failed 状态。
猜你喜欢
  • 1970-01-01
  • 2018-12-12
  • 2015-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多