[javascript] Chai가 비동기 Mocha 테스트로 작업하게하는 방법이 있습니까?

Browser Runner를 사용하여 Mocha에서 비동기 테스트를 실행하고 있으며 Chai의 기대 스타일 어설 션을 사용하려고합니다.

window.expect = chai.expect;
describe('my test', function() {
  it('should do something', function (done) {
    setTimeout(function () {
      expect(true).to.equal(false);
    }, 100);
  }
}

이것은 나에게 정상적인 실패한 주장 메시지를주지 않고 대신 나는 얻는다.

Error: the string "Uncaught AssertionError: expected true to equal false" was thrown, throw an Error :)
    at Runner.fail (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3475:11)
    at Runner.uncaught (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3748:8)
    at uncaught (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3778:10)

따라서 분명히 오류를 포착하고 있으며 올바르게 표시하지 않습니다. 이 작업을 수행하는 방법에 대한 아이디어가 있습니까? 오류 객체를 사용하여 “done”이라고 부를 수 있지만 Chai와 같은 것의 모든 우아함을 잃고 매우 투박해집니다.



답변

비동기 테스트는 실패한 경우 예외가 의 범위 외부로 throw expect()되어 캡처 할 수없는 예외를 생성합니다 .it()it()

표시되는 캡처 된 예외는 process.on('uncaughtException')노드 아래 또는 window.onerror()브라우저에서를 사용하여 캡처됩니다 .

이 문제를 해결하려면 예외를 첫 번째 매개 변수로 setTimeout()호출하기 위해에서 호출 한 비동기 함수 내에서 예외를 캡처해야합니다 done(). 또한 done()성공을 나타 내기 위해 매개 변수없이 호출해야합니다 . 그렇지 않으면 테스트 함수가 완료되었음을 알리지 않았기 때문에 mocha가 시간 초과 오류를보고합니다.

window.expect = chai.expect;

describe( 'my test', function() {
  it( 'should do something', function ( done ) {
    // done() is provided by it() to indicate asynchronous completion
    // call done() with no parameter to indicate that it() is done() and successful
    // or with an error to indicate that it() failed
    setTimeout( function () {
      // Called from the event loop, not it()
      // So only the event loop could capture uncaught exceptions from here
      try {
        expect( true ).to.equal( false );
        done(); // success: call done with no parameter to indicate that it() is done()
      } catch( e ) {
        done( e ); // failure: call done with an error Object to indicate that it() failed
      }
    }, 100 );
    // returns immediately after setting timeout
    // so it() can no longer catch exception happening asynchronously
  }
}

모든 테스트 케이스에서 그렇게하는 것은 DRY가 아니라 성가신 일이므로이를 수행하는 기능을 제공 할 수 있습니다. 이 함수를 호출합시다 check().

function check( done, f ) {
  try {
    f();
    done();
  } catch( e ) {
    done( e );
  }
}

으로 check()다음과 같이 이제 비동기 테스트를 다시 작성할 수 있습니다 :

window.expect = chai.expect;

describe( 'my test', function() {
  it( 'should do something', function( done ) {
    setTimeout( function () {
      check( done, function() {
        expect( true ).to.equal( false );
      } );
    }, 100 );
  }
}


답변

다음은 ES6 / ES2015 약속 및 ES7 / ES2016 async / await에 대한 통과 테스트입니다. 이 주제를 조사하는 모든 사람에게 멋진 업데이트 된 답변을 제공하기를 바랍니다.

import { expect } from 'chai'

describe('Mocha', () => {
  it('works synchronously', () => {
    expect(true).to.equal(true)
  })

  it('works ansyncronously', done => {
    setTimeout(() => {
      expect(true).to.equal(true)
      done()
    }, 4)
  })

  it('throws errors synchronously', () => {
    return true
    throw new Error('it works')
  })

  it('throws errors ansyncronously', done => {
    setTimeout(() => {
      return done()
      done(new Error('it works'))
    }, 4)
  })

  it('uses promises', () => {
    var testPromise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('Hello')
      }, 4)
    })

    testPromise.then(result => {
      expect(result).to.equal('Hello')
    }, reason => {
      throw new Error(reason)
    })
  })

  it('uses es7 async/await', async (done) => {
    const testPromise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('Hello')
      }, 4)
    })

    try {
      const result = await testPromise
      expect(result).to.equal('Hello')
      done()
    } catch(err) {
      done(err)
    }
  })

  /*
  *  Higher-order function for use with async/await (last test)
  */
  const mochaAsync = fn => {
    return async (done) => {
      try {
        await fn()
        done()
      } catch (err) {
        done(err)
      }
    }
  }

  it('uses a higher order function wrap around async', mochaAsync(async () => {
    const testPromise = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('Hello')
      }, 4)
    })

    expect(await testPromise).to.equal('Hello')
  }))
})


답변

약속이 마음에 들면 Chai as Promised + Q를 사용해보십시오 .

doSomethingAsync().should.eventually.equal("foo").notify(done);


답변

나는 Mocha 메일 링리스트에서 같은 것을 물었다. 그들은 기본적으로 이렇게 말했습니다. Mocha와 Chai로 비동기 테스트를 작성하기 위해 :

  • 항상 테스트 시작 if (err) done(err);
  • 항상 done().

그것은 내 문제를 해결했고 사이에 내 코드의 한 줄을 변경하지 않았습니다 (Chai 기대치). 이것은 setTimout비동기 테스트를 수행하는 방법이 아닙니다.

다음 은 메일 링리스트에있는 토론 링크 입니다.


답변

이 문제를 해결하는 패키지를 게시했습니다.

먼저 check-chai패키지를 설치하십시오 .

npm install --save check-chai

그런 다음 테스트에서 아래와 같이 도우미 기능 chai.use(checkChai);을 사용하고 사용합니다 chai.check.

var chai = require('chai');
var dirtyChai = require('dirty-chai');
var checkChai = require('check-chai');
var expect = chai.expect;
chai.use(dirtyChai);
chai.use(checkChai);

describe('test', function() {

  it('should do something', function(done) {

    // imagine you have some API call here
    // and it returns (err, res, body)
    var err = null;
    var res = {};
    var body = {};

    chai.check(done, function() {
      expect(err).to.be.a('null');
      expect(res).to.be.an('object');
      expect(body).to.be.an('object');
    });

  });

});

Per Chai가 비동기 Mocha 테스트로 작업하게하는 방법이 있습니까? 나는 이것을 NPM 패키지로 게시했습니다.

자세한 내용은 https://github.com/niftylettuce/check-chai 를 참조하십시오.


답변

chaiAsPromised를 사용해보세요! 뛰어난 이름을 갖는 것 외에도 다음과 같은 명령문을 사용할 수 있습니다.

expect(asyncToResultingValue()).to.eventually.equal(true)

확인할 수 있으며, Mocha + Chai에서 매우 잘 작동합니다.

https://github.com/domenic/chai-as-promised


답변

Jean Vincent의 답변 과 매우 관련이 있고 영감을 받아 그의 check함수 와 유사한 도우미 함수를 사용 하지만 eventually대신 호출합니다 (이것은 약속 된 차이의 명명 규칙과 일치하는 데 도움이됩니다). 임의의 수의 인수를 사용하여 원래 콜백에 전달하는 함수를 반환합니다. 이를 통해 테스트에서 추가 중첩 함수 블록을 제거하고 모든 유형의 비동기 콜백을 처리 할 수 ​​있습니다. 여기 ES2015로 작성되었습니다.

function eventually(done, fn) {
  return (...args) => {
    try {
      fn(...args);
      done();
    } catch (err) {
      done(err);
    }
  };
};

사용 예 :

describe("my async test", function() {
  it("should fail", function(done) {
    setTimeout(eventually(done, (param1, param2) => {
      assert.equal(param1, "foo");   // this should pass
      assert.equal(param2, "bogus"); // this should fail
    }), 100, "foo", "bar");
  });
});