如何用mocha和chai正确测试承诺?

cho*_*ovy 140 mocha.js node.js promise chai

以下测试表现得很奇怪:

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);
        })
});
Run Code Online (Sandbox Code Playgroud)

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

我该如何正确处理失败的测试(即: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
    });
};
Run Code Online (Sandbox Code Playgroud)

Ben*_*aum 219

最简单的方法是使用Mocha在最近版本中内置的promises支持:

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
});
Run Code Online (Sandbox Code Playgroud)

或者使用现代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);
});
Run Code Online (Sandbox Code Playgroud)

由于这种方法是端到端的承诺,因此更容易测试,你不必考虑你正在考虑的奇怪案例,就像done()各处的奇怪电话一样.

这是Mocha目前在Jasmine等其他图书馆的优势.您可能还想检查Chai As Promised,这会让它更容易(不.then)但我个人更喜欢当前版本的清晰度和简洁性

  • @Scott不会在`it`中选择一个`done`参数来退出它. (14认同)
  • 很棒的答案,工作完美.回顾文档,它就在那里 - 我想是很容易错过的.`或者,您可以返回Promise,而不是使用done()回调.如果您正在测试的API返回promises而不是回调,那么这很有用:` (5认同)
  • 在什么版本的摩卡这个开始?当尝试使用mocha 2.2.5执行此操作时,我得到一个`确保在此test`中调用done()回调错误. (4认同)
  • 和斯科特一样有问题.我没有将`done`参数传递给`it`调用,这仍然在发生...... (4认同)
  • 这对我很有帮助.在我的`it`回调中删除`done`,并在回调中显式调用`return`(在promise上)是我如何使它工作,就像在代码片段中一样. (2认同)
  • 这还应该包括如何验证承诺在预期时会被拒绝。 (2认同)

fea*_*ool 40

正如已经在这里指出的那样,较新版本的Mocha已经是Promise-aware.但是由于OP专门询问了Chai,所以指出chai-as-promised为测试承诺提供干净语法的软件包是公平的:

使用chai-as-promise

以下是如何使用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');
});
Run Code Online (Sandbox Code Playgroud)

没有柴 - 承诺

为了清楚地说明测试的内容,这里的编码相同的例子没有chai-as-promise:

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'); })
            ;
});
Run Code Online (Sandbox Code Playgroud)

  • 第二种方法的问题是当`expect(s)`之一失败时调用`catch`.这给人一种错误的印象,承诺失败了,即使它没有.这只是失败的预期. (3认同)
  • 神圣的哎呀,谢谢你告诉我,我必须打电话给'Chai.use`来安装它.我从来没有从他们的文档中挑选出来.| :( (2认同)

Fra*_*cke 5

这是我的看法:

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

延迟的 Promise 函数,如果延迟为 0,则失败:

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

阳性测试相当简单。意外失败(通过模拟500\xe2\x86\x920)将自动使测试失败。

\n\n

负测试使用 try-catch-idea。但是:“抱怨”不期望的传递仅发生在 catch 子句之后(这样,它就不会出现在 catch() 子句中,从而触发进一步但误导性的错误。

\n\n

为了使这一策略发挥作用,必须从 catch 子句返回测试。如果您不想测试其他任何内容,请使用另一个 it() 块。

\n