모카와 차이로 약속을 올바르게 테스트하려면 어떻게해야합니까?


148

다음 테스트는 이상하게 작동합니다.

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

답변:


233

가장 쉬운 방법은 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
});

또는 현대 노드와 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과 같은 다른 라이브러리보다 장점입니다. 당신은 또한 확인 할 수 있습니다 차이로이 약속 이 더 쉽게 (더하지 않습니다 것이다 .then)하지만 개인적으로는 현재 버전의 명확성과 단순성을 선호


4
어떤 모카 버전에서이 작업이 시작 되었습니까? 나는 얻을 Ensure the done() callback is being called in this test모카 2.2.5이 작업을 수행 할 때 오류가 발생했습니다.
Scott

14
@Scott 는 옵트 아웃 할 done매개 변수를 사용 하지 않습니다 it.
Benjamin Gruenbaum

2
이것은 나에게 매우 도움이되었다. 제거 done내에서 it콜백, 명시 적으로 호출하는 return콜백에서 (약속은) 내가 그냥 코드에서 같이 작업했던 방식이다.
JohnnyCoder

5
멋진 답변, 완벽하게 작동합니다. 문서를 되돌아 보면 거기에 있습니다. 놓치기 쉽습니다. 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
Scott과 같은 문제가 있습니다. 나는 호출에 done매개 변수를 전달하지 않고 it있으며, 이것은 여전히 ​​일어나고있다.

43

이미 지적한 바와 같이 여기 , 모카의 최신 버전은 이미 약속-알고 있습니다. 그러나 OP가 Chai에 대해 구체적으로 물었으므로 chai-as-promised약속을 테스트하기위한 깔끔한 구문을 제공하는 패키지 를 지적하는 것이 공평합니다 .

약속 된 차이를 사용하여

약속의 약속을 사용 하여 약속에 대한 사례 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');
});

약속 된 차이없이

테스트 대상에 대해 명확히하기 위해 다음은 약속의 여지없이 코딩 된 동일한 예제입니다.

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
두 번째 접근 방식의 문제 catchexpect(s)실패 중 하나가 발생했을 때 발생 한다는 것 입니다 . 이것은 약속이 실패했지만 약속이 실패했다는 잘못된 인상을줍니다. 실패한 것은 기대에 불과합니다.
TheCrazyProgrammer 2016 년

2
Chai.use마운트하기 위해 전화해야한다고 알려 주셔서 감사 합니다. 나는 그들이 가지고있는 문서에서 그것을 선택하지 못했습니다. | :(
Arcym

3

여기 내 테이크가있다 :

  • 사용 async/await
  • 추가 차이 모듈이 필요하지 않음
  • 캐치 문제를 피하면서 @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거부 된 약속이 에스컬레이션되면 예기치 않은 실패 (by by simulate )가 테스트에 자동으로 실패합니다.

부정적인 테스트 는 try-catch-idea를 사용합니다. 그러나 원치 않는 패스에 대한 '신고'는 catch 절 이후에만 발생합니다 (그러면 catch () 절에서 끝나지 않아 잘못된 오류를 유발합니다.

이 전략이 작동하려면 catch 절에서 테스트를 반환해야합니다. 다른 것을 테스트하지 않으려면 다른 it ()-block을 사용하십시오.


2

더 나은 솔루션입니다. 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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.