基于事件的单元测试失败,并显示“done() 被多次调用”

nma*_*rko 5 javascript mocha.js eventemitter

我有一个简单的异步回调测试,我已经设置了mocha

describe('test', function () {
    it('should not work', function(done) {
        client.on('success', function () {
          return done('client saw success message but should have errored');
        });
        client.on('error', function (err) {
          return done();
        });
    });
});
Run Code Online (Sandbox Code Playgroud)

这个想法是客户端执行一些异步操作并且应该接收一个错误事件。如果它收到其他任何东西,那么测试应该失败。

不幸的是,mocha不断抱怨:

done() called multiple times
Run Code Online (Sandbox Code Playgroud)

我已经做了各种各样的事情来验证这不是真的。例如,我尝试done在成功处理程序之前抛出错误,在控制到达成功处理程序时进行记录等。

如何在不告诉我我打了done两次电话的情况下运行此测试?我会抛出一个错误而不是done用错误消息调用,但这会导致测试失败,超时而不是我想要的错误。

Nik*_*des 6

您的测试失败是因为您在测试完成后仍在侦听事件

完成的测试不会自动删除事件侦听器。

在您的下一个测试中,您将再次触发该事件,但之前的测试事件侦听器将再次被调用,因为它们仍在侦听该事件。由于done在测试完成时已经调用了它们,因此它们再次触发,因此您会收到done was called multiple times.

这里有几个选项:

  • 您可以在每次测试后使用命名函数删除事件侦听器。
  • 您可以使用once侦听器。

通过命名函数删除事件侦听器:

describe('test', () => {  
  it('should work', done => {
    const finish = err => {
      done(err)
      client.removeListener('success', finish)
      client.removeListener('error', finish)
    }

    client.on('error', finish)    
    client.on('success', result => {
      result.should.equal('foo')
      // rest of tests for result...

      finish()
    })

    client.fireEvent()
  })  
})
Run Code Online (Sandbox Code Playgroud)

请注意,您可能需要使用offremoveEventListener代替removeListener- 无论您client使用哪种方法来删除侦听器。

使用once监听器:

或者,您可以使用once侦听器来侦听事件。顾名思义,此处理程序仅触发一次,因此之后无需手动删除侦听器。

describe('test', function () {  
  it('should work', done => {

    client.once('error', done)
    client.once('success', result => {
      result.should.equal('foo')
      // rest of tests for result...

      done()
    })

    client.fireEvent()
  })
})
Run Code Online (Sandbox Code Playgroud)

警告:这些方法有一个重要的警告。它们不允许您测试边缘情况是否client真的只触发一次事件。如果多次client错误地触发success,您的测试也会错误地成功。我不确定此时您如何优雅地处理这个问题,但欢迎提出意见。