Làm thế nào để tôi kiểm tra đúng lời hứa với mocha và chai?


148

Bài kiểm tra sau đây có hành vi kỳ quặc:

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

Làm thế nào tôi nên xử lý đúng một lời hứa bị từ chối (và kiểm tra nó)?

Làm thế nào tôi nên xử lý đúng một bài kiểm tra thất bại (ví dụ : expect(data.rate).to.have.length(400);?

Đây là triển khai tôi đang thử nghiệm:

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

Câu trả lời:


233

Cách dễ nhất để làm là sử dụng hỗ trợ hứa hẹn tích hợp mà Mocha có trong các phiên bản gần đây:

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

Hoặc với Node hiện đại và 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);
});

Vì cách tiếp cận này hứa hẹn sẽ kết thúc dễ dàng hơn để kiểm tra và bạn sẽ không phải suy nghĩ về những trường hợp lạ mà bạn đang nghĩ giống như những done()cuộc gọi kỳ lạ ở khắp mọi nơi.

Đây là một lợi thế Mocha có hơn các thư viện khác như Jasmine vào lúc này. Bạn cũng có thể muốn kiểm tra Chai As Promised sẽ giúp việc này trở nên dễ dàng hơn (không .then) nhưng cá nhân tôi thích sự rõ ràng và đơn giản của phiên bản hiện tại


4
Phiên bản Mocha nào đã bắt đầu? Tôi gặp Ensure the done() callback is being called in this testlỗi khi thử làm điều này với mocha 2.2.5.
Scott

14
@Scott không lấy donetham số trong itđó sẽ từ chối tham số đó.
Benjamin Gruenbaum

2
Điều này rất hữu ích với tôi. Loại bỏ donetrong itcuộc gọi lại của tôi và gọi một cách rõ ràng return(về lời hứa) trong cuộc gọi lại là cách tôi làm cho nó hoạt động, giống như trong đoạn mã.
JohnnyCoder

5
Câu trả lời tuyệt vời, hoạt động hoàn hảo. Nhìn lại các tài liệu, nó ở đó - chỉ dễ bỏ lỡ tôi đoán. 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:
Federico

4
Có cùng một vấn đề như Scott. Tôi không chuyển donetham số cho itcuộc gọi và điều này vẫn đang diễn ra ...

43

Như đã chỉ ra ở đây , các phiên bản mới hơn của Mocha đã được Promise nhận biết. Nhưng vì OP đã hỏi cụ thể về Chai, nên thật công bằng khi chỉ ra chai-as-promisedgói cung cấp cú pháp rõ ràng để thử nghiệm lời hứa:

sử dụng chai như đã hứa

Đây là cách bạn có thể sử dụng chai như đã hứa để kiểm tra cả hai trường hợp resolverejectcho Lời hứa:

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');
});

không có chai như đã hứa

Để làm cho nó thực sự rõ ràng về những gì đang được thử nghiệm, đây là ví dụ tương tự được mã hóa mà không có chai như đã hứa:

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'); })
            ;
});

5
Vấn đề với cách tiếp cận thứ hai là catchđược viện dẫn khi một trong những expect(s)thất bại. Điều này mang lại một ấn tượng sai lầm rằng lời hứa đã thất bại mặc dù nó đã không xảy ra. Đó chỉ là sự mong đợi mà thất bại.
TheCrazyProgrammer

2
Holy heck cảm ơn vì đã nói với tôi rằng tôi phải gọi Chai.useđể gắn kết nó. Tôi chưa bao giờ nhặt được nó từ tài liệu họ có. | :(
Arcym

3

Đây là của tôi:

  • sử dụng async/await
  • không cần thêm mô-đun chai
  • tránh vấn đề bắt, @TheCrazyProgrammer đã chỉ ra ở trên

Hàm hứa hẹn bị trì hoãn, không thành công, nếu bị trễ 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')
})

Kiểm tra tích cực là khá đơn giản. Thất bại bất ngờ (mô phỏng bởi 500→0) sẽ thất bại trong bài kiểm tra, vì lời hứa bị từ chối leo thang.

Thử nghiệm tiêu cực sử dụng ý tưởng thử bắt. Tuy nhiên: 'phàn nàn' về việc vượt qua không mong muốn chỉ xảy ra sau mệnh đề bắt (theo cách đó, nó không kết thúc trong mệnh đề Catch (), gây ra thêm các lỗi sai nhưng gây hiểu lầm.

Để chiến lược này hoạt động, người ta phải trả lại bài kiểm tra từ mệnh đề bắt. Nếu bạn không muốn kiểm tra bất cứ điều gì khác, hãy sử dụng khối khác () -.


2

Thre là một giải pháp tốt hơn. Chỉ cần trả lại lỗi với thực hiện trong một khối bắt.

// ...

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

kiểm tra này sẽ thất bại với thông báo sau: AssertionError: expected 1 to equal 11

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.