【问题标题】:How do I properly test promises with mocha and chai?如何使用 mocha 和 chai 正确测试 Promise?
【发布时间】:2014-12-21 16:09:41
【问题描述】:

以下测试表现异常:

it('Should return the exchange rates for btc_ltc', function(done) {
    var pair = 'btc_ltc';

    shapeshift.getRate(pair)
        .then(function(data){
            expect(data.pair).to.equal(pair);
            expect(data.rate).to.have.length(400);
            done();
        })
        .catch(function(err){
            //this should really be `.catch` for a failed request, but
            //instead it looks like chai is picking this up when a test fails
            done(err);
        })
});

我应该如何正确处理被拒绝的承诺(并对其进行测试)?

我应该如何正确处理失败的测试(即:expect(data.rate).to.have.length(400);

这是我正在测试的实现:

var requestp = require('request-promise');
var shapeshift = module.exports = {};
var url = 'http://shapeshift.io';

shapeshift.getRate = function(pair){
    return requestp({
        url: url + '/rate/' + pair,
        json: true
    });
};

【问题讨论】:

    标签: node.js promise mocha.js chai


    【解决方案1】:

    有一个更好的解决方案。只需在 catch 块中使用 done 返回错误即可。

    // ...
    
    it('fail', (done) => {
      // any async call that will return a Promise 
      ajaxJson({})
      .then((req) => {
        expect(1).to.equal(11); //this will throw a error
        done(); //this will resove the test if there is no error
      }).catch((e) => {
        done(e); //this will catch the thrown error
      }); 
    });
    

    此测试将失败并显示以下消息:AssertionError: expected 1 to equal 11

    【讨论】:

      【解决方案2】:

      这是我的看法:

      • 使用async/await
      • 不需要额外的 chai 模块
      • 避免捕获问题,@TheCrazyProgrammer 在上面指出

      延迟承诺函数,如果延迟为 0,则失败:

      const timeoutPromise = (time) => {
          return new Promise((resolve, reject) => {
              if (time === 0)
                  reject({ 'message': 'invalid time 0' })
              setTimeout(() => resolve('done', time))
          })
      }
      
      //                     ↓ ↓ ↓
      it('promise selftest', async () => {
      
          // positive test
          let r = await timeoutPromise(500)
          assert.equal(r, 'done')
      
          // negative test
          try {
              await timeoutPromise(0)
              // a failing assert here is a bad idea, since it would lead into the catch clause…
          } catch (err) {
              // optional, check for specific error (or error.type, error. message to contain …)
              assert.deepEqual(err, { 'message': 'invalid time 0' })
              return  // this is important
          }
          assert.isOk(false, 'timeOut must throw')
          log('last')
      })
      

      阳性测试相当简单。意外失败(由500→0 模拟)将自动失败测试,​​因为被拒绝的承诺升级。

      否定测试使用 try-catch-idea。但是:“抱怨”不希望的通过仅发生在 catch 子句之后(这样,它不会在 catch() 子句中结束,从而引发更多但误导性的错误。

      要使这种策略起作用,必须从 catch 子句返回测试。如果您不想测试任何其他内容,请使用另一个 it()-block。

      【讨论】:

        【解决方案3】:

        最简单的做法是使用 Mocha 在最新版本中提供的内置承诺支持:

        it('Should return the exchange rates for btc_ltc', function() { // no done
            var pair = 'btc_ltc';
            // note the return
            return shapeshift.getRate(pair).then(function(data){
                expect(data.pair).to.equal(pair);
                expect(data.rate).to.have.length(400);
            });// no catch, it'll figure it out since the promise is rejected
        });
        

        或者使用现代 Node 和 async/await:

        it('Should return the exchange rates for btc_ltc', async () => { // no done
            const pair = 'btc_ltc';
            const data = await shapeshift.getRate(pair);
            expect(data.pair).to.equal(pair);
            expect(data.rate).to.have.length(400);
        });
        

        由于这种方法是端到端的承诺,因此更容易测试,而且您不必考虑您正在考虑的奇怪情况,例如随处可见的奇怪的 done() 调用。

        这是 Mocha 目前相对于 Jasmine 等其他库的优势。您可能还想查看Chai As Promised,这会更容易(不是.then),但我个人更喜欢当前版本的清晰和简单

        【讨论】:

        • 这是从什么版本的摩卡开始的?尝试使用 mocha 2.2.5 执行此操作时出现Ensure the done() callback is being called in this test 错误。
        • @Scott 不要在it 中使用done 参数,否则会选择退出。
        • 这对我很有帮助。在我的it 回调中删除done,并在回调中显式调用return(承诺)是我让它工作的方式,就像在代码sn-p中一样。
        • 很棒的答案,完美。回顾文档,它就在那里——我猜很容易错过。 Alternately, instead of using the done() callback, you may return a Promise. This is useful if the APIs you are testing return promises instead of taking callbacks:
        • 和 Scott 有同样的问题。我没有将done 参数传递给it 调用,这仍在发生......
        【解决方案4】:

        正如here 已经指出的那样,Mocha 的较新版本已经支持 Promise。但是由于 OP 专门询问了 Chai,因此指出 chai-as-promised 包是公平的,它为测试 Promise 提供了干净的语法:

        使用 chai-as-promised

        以下是使用 chai-as-promised 测试 Promise 的 resolvereject 案例的方法:

        var chai = require('chai');
        var expect = chai.expect;
        var chaiAsPromised = require("chai-as-promised");
        chai.use(chaiAsPromised);
        
        ...
        
        it('resolves as promised', function() {
            return expect(Promise.resolve('woof')).to.eventually.equal('woof');
        });
        
        it('rejects as promised', function() {
            return expect(Promise.reject('caw')).to.be.rejectedWith('caw');
        });
        

        没有承诺的柴

        为了明确要测试的内容,下面是相同的示例,但没有按承诺使用 chai-as-promised:

        it('resolves as promised', function() {
            return Promise.resolve("woof")
                .then(function(m) { expect(m).to.equal('woof'); })
                .catch(function(m) { throw new Error('was not supposed to fail'); })
                    ;
        });
        
        it('rejects as promised', function() {
            return Promise.reject("caw")
                .then(function(m) { throw new Error('was not supposed to succeed'); })
                .catch(function(m) { expect(m).to.equal('caw'); })
                    ;
        });
        

        【讨论】:

        • 第二种方法的问题是catchexpect(s) 之一失败时被调用。这给人一种错误的印象,即即使没有承诺也失败了。失败的只是期望。
        • 感谢您告诉我我必须致电 Chai.use 来安装它。我永远不会从他们拥有的文档中找到它。 |:(
        猜你喜欢
        • 2015-12-01
        • 2019-02-04
        • 1970-01-01
        • 2019-07-18
        • 1970-01-01
        • 2013-04-11
        • 2018-05-05
        • 2018-02-27
        相关资源
        最近更新 更多