Mocha: 异步测试因超时而不是断言错误而失败

创建于 2014-02-05  ·  25评论  ·  资料来源: mochajs/mocha

本次测试:

    it('returns the correct value', function(done) {
      var returnValue = 5;

      aPromise.then(function() {
        expect(returnValue).to.equal(42);
        done();
      });

    });

这个测试失败了timeout of 2000ms exceeded而不是断言错误。 我猜这是因为expect()调用会引发错误,而done()永远不会被执行,我想知道是否有更好的方法来测试这种代码。

最有用的评论

我遇到了类似的问题,最终意识到在使用 promise 测试异步函数时不应该使用 done,而应该返回 promise。 所以你应该能够在没有超时的情况下做到这一点,例如:

it('returns the correct value', function() {
    var returnValue = 5;

    return aPromise.then(function() {
        expect(returnValue).to.equal(42);
    });
});

所有25条评论

aPromise是否曾经解决过? 如果没有,除了暂停,别无选择。

@NickHeiner是的,它解决了; 然后expect()发现returnValue不是equal(42)并抛出。

@gurdiga如果出现超时而不是断言错误,你怎么知道它会抛出?

@hallas @NickHeiner这是正在运行的东西: http :

@gurdiga在我看来,你的承诺有自己的错误捕捉。 尝试在您的承诺中添加.catch(done) ,我认为它会按预期工作。

@hallas Wow:_that_ 就是答案! :) aPromise.finally()似乎非常适合将done放入:它也需要在承诺解决时调用。 ;)

谢谢!

我觉得我好笨。

我想我终于明白了:当任何承诺的处理函数中出现任何问题时,无论是传递给.then().catch()还是.finally()的函数,错误都是由承诺库处理。 这样,测试运行器永远不会看到真正的错误, done()永远不会被调用(因为在它抛出断言错误之前的某些东西),因此超时错误就是您从测试运行器中获得的全部。

为了摆脱承诺,我使用setTimeout()

    it('returns the correct value', function(done) {
      var returnValue = 5;

      aPromise.then(function() {
        setTimeout(function() {
          expect(returnValue).to.equal(42);
          done();
        });
      });
    });

我发现这是获取正确错误消息和测试运行器行为的唯一方法。

done传递给.catch().finally() ,测试在任何情况下都被视为通过,因此如果存在断言错误,您将永远不会看到它们。

我遇到了类似的问题,最终意识到在使用 promise 测试异步函数时不应该使用 done,而应该返回 promise。 所以你应该能够在没有超时的情况下做到这一点,例如:

it('returns the correct value', function() {
    var returnValue = 5;

    return aPromise.then(function() {
        expect(returnValue).to.equal(42);
    });
});

我认为这个问题仍然存在。 我遇到了超时问题,无法通过返回承诺来解决,因为我的模块不使用承诺。

it("works", function(done) {
    new Something()
    .on("eventA", function(result) {
        expect(result).to.be.true;
    })
    .on("eventB", function(result) {
        expect(result).to.be.false;
        done();
    });
});
  • 将实例包装在Promise似乎有些过分。
  • 将每个断言包装在try / catch似乎也有些过分,更重要的是,会导致Error: done() called multiple times

想法:
http://staxmanade.com/2015/11/testing-asyncronous-code-with-mochajs-and-es7-async-await/

至于博客文章的建议,我不知道异步函数错误处理,但对于普通承诺来说,try-catch 建议很奇怪:没有 try-catch 的原始承诺尝试几乎是正确的,它只需要使用.catch(done)而不是使用then的第二个参数作为done 。 (当然,由于 Mocha 直接支持 promise,您也可以只返回 promise,但对于它的价值...)最初的 promise 示例中的问题不是缺少 try-catch,而是第二个处理程序to then不会被第一个处理程序抛出的异常调用,而后面的catch是; 我不知道以这种方式设计 Promise 的理由是什么,但 Promise 就是这样。 此外,如果由于某种原因有多个then ,则只需要一个最终的.catch(done) ,这是处理程序内部的 try-catch 的一个优势点(在事实上, .catch(done)开始就不那么样板了)。

至于你的API:

  1. 您确定这两个事件都被调用,并且顺序正确吗? 如果不是,您的测试如何将其转变为正常失败?
  2. 从事件处理程序抛出的异常通常会发生什么? 如果它们不像在同步代码中那样传播,而是打算在 API 上监听(例如使用.on("error", function(error) {...}) ),那么它们将永远不会到达 Mocha,除非您监听它们并且有侦听器调用done并带有错误(或者,如果侦听器将错误作为其第一个参数传递给侦听器,则仅使用done与侦听器一起使用,例如.on("error", done) 。大概这也只会需要在每个测试中编写一次,而不是每个事件处理程序编写一次,就像承诺中的.catch(done)
  1. 是的,我使用"end" / "drain"事件来检查是否设置了其他事件中的布尔值。
  2. 超时发生。 我正在努力寻找一种精简而干净的替代品。

抱歉,我还是不知道你的 API 应该如何报告错误。

是的,到目前为止,我一直依赖于 _when_ 某些事情失败的超时,然后手动挖掘以找出 _how/why_。 幸运的是,事情很少会出问题,但这并不是缺乏更好设计的借口(主要是我自己)。

@stevenvachon :请提前原谅我,但我看不出你的例子有直接的问题。 在你的事件监听器中做出的断言应该由 Mocha 通过uncaughtException映射处理(除非事件发射器实现捕捉监听器错误并发出error事件或其他东西,然后仍然很容易解决)。

现在,如果您在幕后的实现使用 Promise,但发出事件而不是公开 Promise,则您的断言确实会被“吃掉”。 我解决这个问题的方法是使用unhandledRejection

我通常把它放在一个在我的测试之前运行的设置脚本中:

process.on('unhandledRejection', function (reason)
{
    throw reason;
});

注意:这可能需要一些额外的肘部润滑脂才能在浏览器中工作。

我希望看到 Mocha 像uncaughtException那样支持它,因为这是一个常见的用例; 仅仅因为我使用 Promises 并不意味着我想将它们返回给调用者!

[email protected]有同样的问题

    it('Convert files into base64', (resolve) => {
        let files = Promise.all(promises);

        return files
            .then(([actual, expected]) => {
                assert.equal(actual, expected, 'Files not are equal');
                resolve();
            })
            .catch(error => resolve);
    });
   Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.

.catch是错误的。 error => resolve等价于function(error) { return resolve } ,这意味着resolve不会被调用并且错误被忽略。 您想要的是使用错误调用resolve ,即error => resolve(error) 。 当然,传递一个回调函数X ,它简单地调用Y函数,其参数与调用X参数相同,相当于仅传递Y作为回调,所以即使.catch(error => resolve(error))也可以简化为.catch(resolve) 。 (如果您将它传递给then ,则只需不要直接传递resolve ,因此,需要避免将then的结果参数传递给resolve以防止将其视为错误: then(()=>resolve())而不仅仅是.then(resolve) ;但是由于您使用then回调进行断言,因此不会出现.)

(此外,惯用地,这里的resolve可能应该按照done方式命名,因为它同时处理成功和失败,并根据是否使用参数调用或不是。因此,Mocha 错误消息中的名称。但这可能是一个有争议的问题;请继续阅读。)

但是,在这种情况下,您可以通过仅返回承诺而不使用 test done 参数来进一步简化它,因为 Mocha 将等待承诺成功或失败以指示测试成功或失败(前提是没有 done 参数)测试函数;在两者都使用的情况下的行为仍在散列中):

it('Convert files into base64', () => {
    let files = Promise.all(promises);
    return files
        .then(([actual, expected]) => {
            assert.equal(actual, expected, 'Files not are equal');
        })
});

@lsphillips对我

我花了一段时间才解决这个问题! 根据以上答案,以下是两个选项:

npm install --save mocha expect.js q
./node_modules/mocha/bin/mocha test.spec.js

// test.spec.js

var $q = require('q');
var expect = require('expect.js');

describe('tests with done', function(){
    it('returns the correct value from promise', function(done) {
      var returnValue = 5;
      var def = $q.defer();
      def.promise.then((val) => {
        expect(val).to.equal(42);
        done();
      }).catch(done);
      def.resolve(returnValue)
    });
})

describe('tests returning promises', function(){
    it('returns the correct value from promise', function() {
      var returnValue = 5;
      var def = $q.defer();
      def.resolve(returnValue)
      return def.promise.then((val) => {
        expect(val).to.equal(42);
      });
    });
})
  tests with done
    1) returns the correct value from promise

  tests returning promises
    2) returns the correct value from promise


  0 passing (15ms)
  2 failing

  1) tests with done returns the correct value from promise:
     Error: expected 5 to equal 42
      at Assertion.assert (node_modules/expect.js/index.js:96:13)
      at Assertion.be.Assertion.equal (node_modules/expect.js/index.js:216:10)
      at def.promise.then (tests/test.spec.js:9:24)
      at _fulfilled (node_modules/q/q.js:854:54)
      at self.promiseDispatch.done (node_modules/q/q.js:883:30)
      at Promise.promise.promiseDispatch (node_modules/q/q.js:816:13)
      at node_modules/q/q.js:570:49
      at runSingle (node_modules/q/q.js:137:13)
      at flush (node_modules/q/q.js:125:13)
      at _combinedTickCallback (internal/process/next_tick.js:67:7)
      at process._tickCallback (internal/process/next_tick.js:98:9)

  2) tests returning promises returns the correct value from promise:
     Error: expected 5 to equal 42
      at Assertion.assert (node_modules/expect.js/index.js:96:13)
      at Assertion.be.Assertion.equal (node_modules/expect.js/index.js:216:10)
      at def.promise.then (tests/test.spec.js:22:24)
      at _fulfilled (node_modules/q/q.js:854:54)
      at self.promiseDispatch.done (node_modules/q/q.js:883:30)
      at Promise.promise.promiseDispatch (node_modules/q/q.js:816:13)
      at node_modules/q/q.js:570:49
      at runSingle (node_modules/q/q.js:137:13)
      at flush (node_modules/q/q.js:125:13)
      at _combinedTickCallback (internal/process/next_tick.js:67:7)
      at process._tickCallback (internal/process/next_tick.js:98:9)

@gurdiga感谢您的 setTimeout() 想法! 我有一个类似的问题,但现在我至少可以得到正确的错误消息!

在我的场景中,我使用 Nightmare 进行 end2end 测试。 我的解决方案是使用.catch(done) 。 您可以在其他 catch 回调中调用done(error)如下例所示。

describe('Clicking in any bad reputation tag', () => {
    it('open the bad reputation modal', (done) => {
      nightmare
        .select('#per-page', '50')
        .waitForAjax()
        .click('[data-reputation="bad"]')
        .evaluate(function() {
          return document.querySelector('.vue-modal .ls-modal-title').innerText
        })
        .then(function(title) {
          title.should.equal('Sua segmentação teve uma avaliação ruim!')
          done()
        })
        .catch((error) => {
          screenshot(nightmare)
          done(error)
        })
    })
  })

@itumoraes有效,但您可以改为这样做:

describe('Clicking in any bad reputation tag', () => {
    it('open the bad reputation modal', () => {
      return nightmare
        .select('#per-page', '50')
        .waitForAjax()
        .click('[data-reputation="bad"]')
        .evaluate(function() {
          return document.querySelector('.vue-modal .ls-modal-title').innerText
        })
        .then(function(title) {
          title.should.equal('Sua segmentação teve uma avaliação ruim!')
        })
    })
  })

如果您返回承诺,则无需调用done() 。 请参阅我的博客文章将 Async/Await 与 Mocha、Express 和 Mongoose 结合使用

我使用了下面的脚本,但我得到了相同的超时超过错误。

我的脚本:

描述(“getBillingDetail”,异步函数(){
this.timeout(55000);
it.only("检查给定的有效作业名",异步函数(完成){
this.timeout(55000);
var 结果 = 等待 url.getBillingDetail('12254785565647858');
控制台日志(结果);
assert.equal(result,true);
});
});

错误:超过 55000 毫秒超时。 对于异步测试和钩子,确保调用“done()”; 如果返回 Promise,请确保它已解决。

不要在多个已解决的问题中拼写相同的东西。 不要将完成的回调传递给异步函数。 阅读有关异步测试的文档

@Munter我删除了完成的回调,但这些错误再次发生

看起来你的承诺从未兑现。

此页面是否有帮助?
0 / 5 - 0 等级