对Nodej中发出的事件进行单元测试的最佳方法是什么?

man*_*ang 28 javascript mocha.js node.js

我正在编写一堆mocha测试,我想测试发出的特定事件.目前,我这样做:

  it('should emit an some_event', function(done){
    myObj.on('some_event',function(){
      assert(true);
      done();
    });
  });
Run Code Online (Sandbox Code Playgroud)

但是,如果事件永远不会发出,它会使测试套件崩溃而不是失败.

测试这个的最佳方法是什么?

Bre*_*and 35

如果您可以保证事件应在一定时间内触发,则只需设置超时.

it('should emit an some_event', function(done){
  this.timeout(1000); //timeout with an error if done() isn't called within one second

  myObj.on('some_event',function(){
    // perform any other assertions you want here
    done();
  });

  // execute some code which should trigger 'some_event' on myObj
});
Run Code Online (Sandbox Code Playgroud)

如果您无法保证事件何时触发,那么它可能不适合进行单元测试.


Myr*_*tol 14

编辑9月30日:

我认为我的答案被认为是正确的答案,但Bret Copeland的技术(见下面的答案)更好,因为它在测试成功时更快,大多数时候你作为测试套件的一部分运行测试就是这种情况.


布雷特科普兰的技术是正确的.您也可以采用不同的方式:

  it('should emit an some_event', function(done){
    var eventFired = false
    setTimeout(function () {
      assert(eventFired, 'Event did not fire in 1000 ms.');
      done();
    }, 1000); //timeout with an error in one second
    myObj.on('some_event',function(){
      eventFired = true
    });
    // do something that should trigger the event
  });
Run Code Online (Sandbox Code Playgroud)

Sinon.js的帮助下,这可以缩短一点.

  it('should emit an some_event', function(done){
    var eventSpy = sinon.spy()
    setTimeout(function () {
      assert(eventSpy.called, 'Event did not fire in 1000ms.');
      assert(eventSpy.calledOnce, 'Event fired more than once');
      done();
    }, 1000); //timeout with an error in one second
    myObj.on('some_event',eventSpy);
    // do something that should trigger the event
  });
Run Code Online (Sandbox Code Playgroud)

在这里,我们检查不仅事件被触发,而且如果事件在超时期间仅触发了一次.

Sinon还支持calledWithcalledOn检查使用了哪些参数和函数上下文.

请注意,如果您希望事件与触发事件的操作同步触发(中间没有异步调用),则可以使用零超时.只有在执行异步调用之间需要很长时间才能完成时,才需要超时1000 ms.很可能不是这样的.

实际上,当事件保证与导致它的操作同步触发时,您可以将代码简化为

  it('should emit an some_event', function() {
    eventSpy = sinon.spy()
    myObj.on('some_event',eventSpy);
    // do something that should trigger the event
    assert(eventSpy.called, 'Event did not fire.');
    assert(eventSpy.calledOnce, 'Event fired more than once');
  });
Run Code Online (Sandbox Code Playgroud)

否则,Bret Copeland的技术在"成功"案例中总是更快(希望是常见的情况),因为done如果事件被触发,它能够立即调用.


Coo*_*lue 5

此方法确保等待的最短时间,但是套件超时设置的最大机会非常干净.

  it('should emit an some_event', function(done){
    myObj.on('some_event', done);
  });
Run Code Online (Sandbox Code Playgroud)

也可以用它来做CPS风格的功能......

  it('should call back when done', function(done){
    myAsyncFunction(options, done);
  });
Run Code Online (Sandbox Code Playgroud)

这个想法也可以this通过放置包装器来扩展以检查更多细节 - 例如参数和- done.例如,由于这个答案,我可以做...

it('asynchronously emits finish after logging is complete', function(done){
    const EE = require('events');
    const testEmitter = new EE();

    var cb = sinon.spy(completed);

    process.nextTick(() => testEmitter.emit('finish'));

    testEmitter.on('finish', cb.bind(null));

    process.nextTick(() => testEmitter.emit('finish'));

    function completed() {

        if(cb.callCount < 2)
            return;

        expect(cb).to.have.been.calledTwice;
        expect(cb).to.have.been.calledOn(null);
        expect(cb).to.have.been.calledWithExactly();

        done()
    }

});
Run Code Online (Sandbox Code Playgroud)