【问题标题】:chai-as-promised: multiple expect statements in a single testchai-as-promised:单个测试中的多个期望语句
【发布时间】:2018-06-01 06:05:20
【问题描述】:

我正在使用chai-as-promised 来测试一些承诺。我的问题是我不确定如何在单个测试中包含多个期望语句。为了让expect().to.be.fulfilled 正常工作,我需要将其退回,如下所示:

it('test', () => {
  return expect(promise).to.be.fulfilled
}

...或者使用notify,像这样:

it('test', (done) => {
  expect(promise).to.be.fulfilled.notify(done)
}

当我有另一件事需要检查时,问题就出现了,例如某个函数被调用,如下所示:

it('test', (done) => {
  var promise = doSomething()
  expect(sinon_function_spy.callCount).to.equal(1)
  expect(promise).to.be.fulfilled.notify(done)
})

这里的问题是,因为doSomething() 是异步的,所以当我调用expect 时,可能还没有调用sinon_function_spy,这使得这个测试变得不稳定。如果我使用then,像这样:

it('test', (done) => {
  var promise = doSomething()
  promise.then(() => {
    expect(sinon_function_spy.callCount).to.equal(1)
  })
  expect(promise).to.be.fulfilled.notify(done)
})

然后测试技术上按预期通过和失败,但由于then调用中抛出的异常,promise被拒绝,它会失败。同样,如果我有一个承诺会被拒绝的情况:

it('test', (done) => {
  var promise = doSomething()
  promise.then(() => {
    expect(sinon_function_spy.callCount).to.equal(1)
  })
  expect(promise).to.be.rejected.notify(done)
})

然后对sinon_function_spy 的检查永远不会被调用,因为承诺被拒绝并且不会调用then

如何让expect 语句可靠地执行并返回正确的值?

【问题讨论】:

  • Mocha 使用异常或承诺拒绝来报告失败。即使使用同步断言,如果第一个断言失败,其余断言也不会运行。我不认为 mocha 真的支持在失败后执行断言。您是否有理由同时执行这两个断言?
  • 是的。我需要检查这两种情况是否都发生了测试用例才能成功。如果 promise 按预期解决,但函数没有被调用,则测试用例不应该通过。

标签: javascript node.js mocha.js chai chai-as-promised


【解决方案1】:

如果您使用 mocha 或 jest 作为测试框架,您可以在 then() 块中返回带有期望的承诺:

it('test', () => {
   return doSomething().then( () => {
     expect(sinon_function_spy.callCount).to.equal(1);
   });
});

在 Promise 成功完成并且 expect 已运行之前,此测试不会结束。如果您使用 jasmine,您可以使用 jasmine-promises 包来获得相同的功能。

对于相反的情况,我建议创建一个包装器来反转承诺的极性:

function reverse( promise ) {
   //use a single then block to avoid issues with both callbacks triggering
   return promise.then(
       () => { throw new Error("Promise should not succeed"); }
       e => e; //resolves the promise with the rejection error
   );
}

现在你可以做

it('test', () => {
   return reverse( doSomethingWrong() ).then( error => {
       expect( error.message ).to.equal("Oh no");
   });
});

【讨论】:

  • 如果我希望承诺能够解决,那就行得通。如果我的测试用例期望 promise 被拒绝怎么办?返回 promise 本身会导致 mocha 将测试用例注册为失败。
【解决方案2】:

如果想要断言 Promise 已实现并且按预期执行了调用,您实际上并不需要将第一部分作为断言。只要你返回它,如果 Promise 拒绝,mocha 测试用例本身就会失败:

it('test', () => {
  return doSomething()
    .then(() => {
      expect(sinon_function_spy.callCount).to.equal(1)
    });
});

如果 doSomething() 返回的 Promise 被拒绝,那么测试用例也将被拒绝。如果expect 断言失败,它也将使带有失败断言的测试用例失败。如果你想更明确一点:

it('test', () => {
  return doSomething()
    .then(() => {
      expect(sinon_function_spy.callCount).to.equal(1)
    }, err => {
      expect(err).to.not.exist;
    });
});

...您可以发现错误。请注意,使用这种带有两个回调的 then 风格,在第一个回调中失败的断言将不会到达第二个回调,因此只有 Mocha 会看到失败的断言。

以下是您如何执行预期失败的 Promise:

it('test', () => {
  return doSomething()
    .then(() => {
      throw new Error('Promise should not have resolved');
    }, err => {
      expect(err).to.exist;
      expect(sinon_function_spy.callCount).to.equal(1)
    });
})

【讨论】:

  • chai-as-promised 非常适合某些场景,但我发现在很多情况下,使用 Promises 比尝试制定使用 chai-as-promised 的测试更容易。
【解决方案3】:

实现多重期望的方法

it('should fail if no auth', () => {
    const promise = chai.request(server).get('/albums');
    return expect(promise).to.be.rejected.then(() => {
      return promise.catch(err => {
        expect(err).not.to.be.null;
        expect(err.response).to.have.status(401);
        expect(err.response).to.be.a.json;
      });
   });
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-05
    • 2015-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-01
    • 1970-01-01
    相关资源
    最近更新 更多