简体   繁体   English

基于事件的单元测试因“done() 被多次调用”而失败

[英]Event-based unit tests fail with “done() called multiple times”

I have a simple async callback test that I've set up with mocha :我使用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();
        });
    });
});

The idea is that the client does some async operation and should receive an error event.这个想法是客户端执行一些异步操作并且应该接收一个错误事件。 If it receives anything else, then the test should fail.如果它收到其他任何东西,那么测试应该失败。

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

done() called multiple times

I've done all sorts of things to verify that this is not true.我已经做了各种各样的事情来验证这不是真的。 For example, I've tried throwing an error before the done in the success handler, logging when control reaches the success handler, etc.例如,我尝试在成功处理程序中的done之前抛出错误,在控制到达成功处理程序时进行记录等。

How can I get this test to run without telling me I'm calling done twice?如何在不告诉我我调用done两次的情况下运行此测试? I would throw an error instead of calling done with an error message, but that would cause the test to fail with a timeout instead of the error I want.我会抛出错误而不是使用错误消息调用done ,但这会导致测试失败并出现超时而不是我想要的错误。

Your tests are failing because you are still listening for events after the test is finished .您的测试失败是因为您在测试完成后仍在侦听事件

A completed test doesn't automatically remove the event listeners.完成的测试不会自动删除事件侦听器。

On your next test you are firing the event again but previous test event listeners are called again since they are still listening for the event.在您的下一个测试中,您将再次触发该事件,但之前的测试事件侦听器将再次被调用,因为它们仍在侦听该事件。 Since done was already called on them when that test completed, they fire again hence you get the error that done was called multiple times .由于在测试完成时已经对它们调用了done ,因此它们再次触发,因此您会得到done was called multiple times的错误。

Couple of options here:这里有几个选项:

  • You can remove the event listeners after each test by using a named function.您可以在每次测试后使用命名函数删除事件侦听器。
  • You can use the once listener.您可以使用once侦听器。

Removing event listeners via a named function:通过命名函数删除事件侦听器:

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()
  })  
})

Note that you might need to use off or removeEventListener instead of removeListener - whichever method your client uses to remove a listener.请注意,您可能需要使用offremoveEventListener而不是removeListener - 无论您的client使用哪种方法来删除侦听器。

Using the once listener:使用once监听器:

Alternatively, you can use the once listener to listen for events.或者,您可以使用once侦听器来侦听事件。 Like the name suggests, this handler fires only once so there's no need to manually remove listeners afterwards.顾名思义,此处理程序仅触发一次,因此之后无需手动删除侦听器。

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()
  })
})

Warning: These methods have an important caveat.警告:这些方法有一个重要的警告。 They don't allow you to test the edge case on whether your client actually fires the event just once .它们不允许您测试边缘情况,以确定您的client是否实际上只触发了一次事件。 If client erroneously fires success more than once your test would erroneously succeed as well.如果client多次错误地触发success ,您的测试也会错误地成功。 I'm not sure how you can handle this gracefully at this time but comments are welcome.我不确定此时您如何优雅地处理这个问题,但欢迎提出意见。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM