简体   繁体   English

调用存根函数时获取 sinon 以解决承诺

[英]Get sinon to resolve a promise when stubbed function is called

I have a set of integration tests for message queueing code (over a real RabbitMQ).我有一组消息队列代码的集成测试(通过真正的 RabbitMQ)。 The tests are verifying that the correct use case is triggered when a specific message arrives.测试正在验证当特定消息到达时是否触发了正确的用例。

it("should trigger the use case", () => {
  const stub = sinon.stub(app, 'useCaseToTrigger').resolves();
  await publishMessage({ id: "SOME_ID" })
  // FAIL - Message isn't processed yet.
  stub.should.have.been.calledWith(match({id: "SOME_ID" }))
})

This doesn't work.这行不通。 The await statement waits for the message to be published, but says nothing of when the message is processed. await语句等待消息被发布,但没有说明消息何时被处理。

I can add a delay, but that quickly becomes a huge waste of time (if there are multiple tests).我可以添加一个延迟,但这很快就会浪费大量时间(如果有多个测试)。

it("should trigger the use case", () => {
  const stub = sinon.stub(app, 'useCaseToTrigger').resolves();
  await publishMessage({ id: "SOME_ID" })
  await new Promise(r => setTimeout(r, 100));
  // PASS - the message has been processed before the verification
  stub.should.have.been.calledWith(match({id: "SOME_ID" }))
})

But if I make the stubbed function resolve a promise when called, I can get my test to work without any undue delay.但是,如果我让存根函数在调用时解决一个承诺,我可以让我的测试工作而不会出现任何不当延迟。

it("should trigger the use case", () => {
  const promise = new Promise(r => {
    sinon.stub(app, "useCaseToTrigger").callsFake(() => {
      setImmediate(() => { r(); }) // Push to the end of the event loop
      return Promise.resolve();
    })
  })
  await publishMessage({ id: "SOME_ID" })
  await promise;
  // PASS - Message processed before continuing test
  app.useCaseToTrigger.should.have.been.calledWith(match({id: "SOME_ID" }))
})

This does become a bit cumbersome to deal with (and probably not the easiest to read either).处理起来确实有点麻烦(而且可能也不是最容易阅读的)。 But it also gives eslint floating promises warnings.但它也给出了 eslint 浮动承诺警告。 So to avoid those warnings, I need to rewrite it to:因此,为了避免这些警告,我需要将其重写为:

it("should trigger the use case", () => {
  await Promise.all([
    new Promise(r => {
      sinon.stub(app, "useCaseToTrigger").callsFake(() => {
        setImmediate(() => { r(); }) // Push to the end of the event loop
        return Promise.resolve();
      })
    }),
    publishMessage({ id: "SOME_ID" })
  ]);
  // PASS - Message processed before continuing test
  app.useCaseToTrigger.should.have.been.calledWith(match({id: "SOME_ID" }))
})

Is there a way to make this cleaner.有没有办法让这个更干净。

I think you can use an empty promise to await, so the app logic eventually resolves that promise when it is called.我认为您可以使用空承诺来等待,因此应用程序逻辑最终会在调用时解决该承诺。 Sinon provides a promise fake that makes this a bit easier than creating a new Promise with an external reference to the resolve function. Sinon 提供了一个假的 promise ,这使得这比使用对resolve函数的外部引用创建一个new Promise更容易。

const p = sinon.promise()
const stub = sinon.stub(app, 'useCaseToTrigger').callsFake(p.resolve)
await publishMessage({ id: "SOME_ID" })
await p
sinon.assert.calledWith(stub, { id: "SOME_ID" })

It looks like sinon .resolves() uses Promise.resolve at the time of the stub call, so it doesn't have a reference to the eventual promise at stub creation, otherwise a stub.promise type of reference might have been useful here to avoid the additional promise setup.看起来 sinon .resolves()在存根调用时使用Promise.resolve ,因此它在存根创建时没有对最终承诺的引用,否则stub.promise类型的引用可能在这里有用避免额外的承诺设置。

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

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