基于事件的单元测试失败 "done() called multiple times"

Event-based unit tests fail with "done() called multiple times"

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

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

不幸的是,mocha一直在抱怨:

done() called multiple times

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

如何在不告诉我我正在调用 done 两次的情况下将此测试进行到 运行?我会抛出错误而不是调用 done 并显示错误消息,但这会导致测试失败并超时而不是我想要的错误。

您的测试失败了,因为您 仍在监听 测试完成.

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

在您的下一次测试中,您将再次触发该事件,但之前的测试事件 监听器会再次被调用,因为他们仍在监听事件。 因为 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()
  })  
})

请注意,您可能需要使用 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()
  })
})

警告:这些方法有一个重要的警告。他们不允许您测试 client 是否真的触发事件 一次 的边缘情况。如果 client 错误地触发 success 不止一次,您的测试也会错误地成功。我不确定你现在如何优雅地处理这个问题,但欢迎发表评论。