在mocha测试中调用异步函数如何避免超时错误:超过2000ms的超时

Posted

技术标签:

【中文标题】在mocha测试中调用异步函数如何避免超时错误:超过2000ms的超时【英文标题】:In mocha testing while calling asynchronous function how to avoid the timeout Error: timeout of 2000ms exceeded 【发布时间】:2013-05-12 12:05:07 【问题描述】:

在我的节点应用程序中,我使用 mocha 来测试我的代码。使用 mocha 调用许多异步函数时,出现超时错误 (Error: timeout of 2000ms exceeded.)。我该如何解决这个问题?

var module = require('../lib/myModule');
var should = require('chai').should();

describe('Testing Module', function() 

    it('Save Data', function(done) 

        this.timeout(15000);

        var data = 
            a: 'aa',
            b: 'bb'
        ;

        module.save(data, function(err, res) 
            should.not.exist(err);
            done();
        );

    );


    it('Get Data By Id', function(done) 

        var id = "28ca9";

        module.get(id, function(err, res) 

            console.log(res);
            should.not.exist(err);
            done();
        );

    );

);

【问题讨论】:

是集成测试吗?运行测试需要很长时间 - 也许您应该考虑存根 - github.com/thlorenz/proxyquire 可能会对您有所帮助。 @surui 谢谢你,我会看的 我可以推荐对异步的东西使用 Promise,然后使用 Chai as promise 测试它是轻而易举的事 【参考方案1】:

您可以在运行测试时设置超时:

mocha --timeout 15000

或者您可以通过编程方式为每个套件或每个测试设置超时:

describe('...', function()
  this.timeout(15000);

  it('...', function(done)
    this.timeout(15000);
    setTimeout(done, 15000);
  );
);

欲了解更多信息,请参阅docs。

【讨论】:

较短的版本是-t。如果你使用 mocha-test 从 grunt 任务运行 mocha,这在选项对象 options:timeout:15000 中也支持。 仅供参考:不鼓励将箭头函数传递给 Mocha。 mochajs.org/#arrow-functions 以上链接中不建议使用箭头函数。它只是说你只需要知道他们做了什么,这样你就不会在需要访问上下文时搞砸了。我从不需要上下文,因为依赖超时是脆弱的,我所有的测试都在几毫秒内运行,但是在使用 sinon-test 时我确实遇到了同样的问题。 99% 的时间仍然使用 lambda。 TypeError: this.timeout is not a function 使用"mocha": "^3.5.0" @adi 你确定你没有使用箭头函数吗?关于 async/await 它在文档中,所以应该可以工作(并且与使用 Promise 相同)。不过听起来像是另一个问题。【参考方案2】:

我发现仅仅增加超时的“解决方案”掩盖了这里真正发生的事情,要么是

    您的代码和/或网络调用太慢(为了获得良好的用户体验,应该低于 100 毫秒) 断言(测试)失败,并且在 Mocha 能够对其采取行动之前,某些东西正在吞噬错误。

当 Mocha 没有从回调中收到断言错误时,您通常会遇到 #2。这是由一些其他代码将异常进一步吞入堆栈引起的。 处理这个问题的正确方法是修复代码而不是吞下错误

当外部代码吞噬你的错误时

如果它是您无法修改的库函数,您需要自己捕获断言错误并将其传递给 Mocha。为此,您可以将断言回调包装在 try/catch 块中,并将任何异常传递给 done 处理程序。

it('should not fail', function (done)  // Pass reference here!

  i_swallow_errors(function (err, result) 
    try  // boilerplate to be able to get the assert failures
      assert.ok(true);
      assert.equal(result, 'bar');
      done();
     catch (error) 
      done(error);
    
  );
);

这个样板当然可以提取到一些实用函数中,以使测试更令人赏心悦目:

it('should not fail', function (done)  // Pass reference here!
    i_swallow_errors(handleError(done, function (err, result) 
        assert.equal(result, 'bar');
    ));
);

// reusable boilerplate to be able to get the assert failures
function handleError(done, fn) 
    try  
        fn();
        done();
     catch (error) 
        done(error);
    

加速网络测试

除此之外,我建议您参考有关开始使用测试存根进行网络调用以使测试通过而不必依赖正常运行的网络的建议。使用 Mocha、Chai 和 Sinon 的测试可能看起来像这样

describe('api tests normally involving network calls', function() 

    beforeEach: function () 
        this.xhr = sinon.useFakeXMLHttpRequest();
        var requests = this.requests = [];

        this.xhr.onCreate = function (xhr) 
            requests.push(xhr);
        ;
    ,

    afterEach: function () 
        this.xhr.restore();
    


    it("should fetch comments from server", function () 
        var callback = sinon.spy();
        myLib.getCommentsFor("/some/article", callback);
        assertEquals(1, this.requests.length);

        this.requests[0].respond(200,  "Content-Type": "application/json" ,
                                 '[ "id": 12, "comment": "Hey there" ]');
        expect(callback.calledWith([ id: 12, comment: "Hey there" ])).to.be.true;
    );

);

请参阅Sinon's nise docs 了解更多信息。

【讨论】:

我有大量的测试,我刚刚检查了我的规范中的所有承诺,以确保他们在承诺结束时都调用done(),我已经在嘲笑使用 Angular 的 $httpBackend 进行网络调用,但没有运气。用 try-catch 包装每个规范似乎不太实用。还有其他建议吗?谢谢! @GustavoMatias 你实际上没有提到你的问题是什么,只是说这不是解决你遇到的任何问题的方法。请详细说明:-) 你的测试失败的速度不够快吗?他们有时会失败,但您想知道为什么?很难猜出你打算达到什么目标。 嗨@oligofren!这确实不是最好的解释。在这里***.com/questions/34510048/…对我的问题有更详细的解释,谢谢! “一般来说,处理这个问题的最简洁(但最丑陋)的方法是用 try/catch 包装你的代码,并将任何异常传递给 done 处理程序。”不,这根本不是最干净的方式。不是由一个长镜头。最干净的方法是编写不会吞下异常的代码。每次我看到有人抱怨 Mocha 没有检测到失败的测试,那是因为有什么东西吞下了异常。添加try.... catch... 可以解决被测代码中的错误,而不是修复它。 @Louis 您可能对这里的原因是正确的,但我无法突然验证它。无论如何,人们对 Mocha 似乎 无法捕捉到一些错误有一个问题,这是一种处理它的方法。您给定的方法假定吞下错误的代码不是某个库函数或类似函数,在这种情况下,它不会那么容易解决。【参考方案3】:

如果您使用箭头函数:

it('should do something', async () => 
  // do your testing
).timeout(15000)

【讨论】:

我试过了,还是不行。我已经输入了.timeout(5000),但该测试仍然出现超过 2000 毫秒的超时错误。【参考方案4】:

有点晚了,但将来有人可以使用它...您可以通过使用以下内容更新 package.json 中的脚本来增加测试超时:

"scripts": "test": "test --timeout 10000" //Adjust to a value you need

使用命令 test 运行您的测试

【讨论】:

为我工作!谢谢你!【参考方案5】:

对我来说,问题实际上是描述函数, 当提供箭头功能时,会导致 mocha 错过 超时,并且行为不一致。 (使用 ES6)

由于没有任何承诺被拒绝,我一直收到这个错误,因为在描述块内失败的不同测试

所以它在不正常工作时的样子:

describe('test', () =>  
 assert(...)
)

这可以使用匿名函数

describe('test', function()  
 assert(...)
)

希望它对某人有所帮助,我的上述配置: (nodejs:8.4.0,npm:5.3.0,mocha:3.3.0)

【讨论】:

【参考方案6】:

我的问题是没有发回响应,所以它挂了。如果您使用的是 express,请确保 res.send(data)、res.json(data) 或您想要使用的任何 api 方法对您正在测试的路由执行。

【讨论】:

【参考方案7】:

确保解决/拒绝测试用例中使用的承诺,无论是间谍还是存根,确保它们解决/拒绝。

【讨论】:

以上是关于在mocha测试中调用异步函数如何避免超时错误:超过2000ms的超时的主要内容,如果未能解决你的问题,请参考以下文章

Mocha 异步测试超时的解决方法 [重复]

使用proxyquire和mocha在单元测试中模拟方法调用时如何模拟时间延迟(超时)?

Mocha,应该 - 在测试具有承诺的异步函数时,断言错误是沉默的

使用 mocha 测试时超时 [重复]

如何使用 mocha 在 reactjs 中测试此异步方法调用

Mocha/Sinon 测试猫鼬里面的快递