有没有办法让Chai与asynchronous摩卡testing一起工作?
我正在Mocha使用Browser Runner运行一些asynchronoustesting,我试图使用Chai的expect风格断言:
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)
所以很明显,这个错误只是显示不正确。 任何想法如何做到这一点? 我想我可以用一个错误对象来调用“完成”,但是随后我失去了所有类似Chai的优雅,并且变得非常笨重。
你的asynchronoustesting会在expect()
失败的时候产生一个exception, it()
不能被it()
捕获,因为这个exception被抛出it()
的范围之外。
您看到显示的捕获exception使用process.on('uncaughtException')
或在浏览器中使用window.onerror()
process.on('uncaughtException')
。
为了解决这个问题,你需要在由setTimeout()
调用的asynchronous函数中捕获exception,以便以exception作为第一个参数调用done()
。 你也需要调用done()
而不用参数来指示成功,否则mocha会报告一个超时错误,因为你的testing函数永远不会发出已经完成的信号:
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 } }
在所有的testing用例上这样做是烦人的,而不是干,所以你可能想提供一个函数来为你做这个。 我们来调用这个函数check()
:
function check( done, f ) { try { f(); done(); } catch( e ) { done( e ); } }
使用check()
你现在可以重写你的asynchronoustesting,如下所示:
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 ); } }
如果你喜欢承诺,尝试柴作为承诺 + Q ,这允许这样的事情:
doSomethingAsync().should.eventually.equal("foo").notify(done);
这是我通过的testingES6 / ES2015承诺和ES7 / ES2016asynchronous/等待。 希望这为任何研究这个主题的人提供了一个很好的更新的答案:
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') })) })
我在Mocha的邮件列表中询问了同样的事情。 他们基本上是这样告诉我的:用摩卡和柴编写asynchronoustesting:
- 总是用
if (err) done(err);
开始testingif (err) done(err);
- 总是用
done()
结束testing。
它解决了我的问题,并没有改变我的代码之间的单行(柴期望等)。 setTimout
不是做asynchronoustesting的方法。
这里是邮件列表中讨论的链接 。
我已经发布了一个解决这个问题的包。
首先安装check-chai
包:
npm install --save check-chai
然后在你的testing中,使用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和asynchronous摩卡testing一起工作? 我把它作为NPM包发布。
有关更多信息,请参阅https://github.com/niftylettuce/check-chai 。
与Jean Vincent的回答非常相关,我们采用了类似于他的check
function的帮助函数,但是我们eventually
调用它(这有助于它与chai-as-promised的命名约定匹配)。 它返回一个函数,它接受任意数量的参数并将它们传递给原始的callback函数。 这有助于消除testing中的额外嵌套function块,并允许您处理任何types的asynchronouscallback。 这里是在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"); }); });
基于@richardforrester http://staxmanade.com/2015/11/testing-asyncronous-code-with-mochajs-and-es7-async-await/提供的链接,如果您省略完成,describe可以使用返回的Promise参数。;
唯一的缺点是必须有一个承诺,没有任何asynchronousfunction(你可以包装一个承诺,你)。 但在这种情况下,代码可以被极大地减less。
它考虑到来自最初的funcThatReturnsAPromise函数或期望的失败:
it('should test Promises', function () { // <= done removed return testee.funcThatReturnsAPromise({'name': 'value'}) // <= return added .then(response => expect(response).to.have.property('ok', 1)); });
我解决了它提取try/catch
函数。
function asyncExpect(test, done){ try{ test(); done(); } catch(error){ done(error); } }
然后在it()
我打电话:
it('shall update a host', function (done) { testee.insertHost({_id: 'host_id'}) .then(response => { asyncExpect(() => { expect(response).to.have.property('ok', 1); expect(response).to.have.property('nModified', 1); }, done); }); });
这也是可debugging的。
尝试chaiAsPromised! 除了出色的命名,你可以使用像这样的语句:
expect(asyncToResultingValue()).to.eventually.equal(true)
可以确认 ,摩卡+柴的效果非常好。
对我来说非常好的工作icm Mocha / Chai是来自Sinon图书馆的假定时器。 只要有必要就在testing中提前计时器。
var sinon = require('sinon'); clock = sinon.useFakeTimers(); // Do whatever. clock.tick( 30000 ); // Advances the JS clock 30 seconds.
有更多的testing完成更快的奖金。
你也可以使用域模块。 例如:
var domain = require('domain').create(); domain.run(function() { // place you code here }); domain.on('error',function(error){ // do something with error or simply print it });