我正在使用浏览器运行器在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)
所以它显然正在捕捉错误,它只是没有正确显示它。任何想法如何做到这一点?我想我可以用一个错误对象叫做“完成”,但后来我失去了像柴这样的东西的优雅,它变得非常笨重......
答案 0 :(得分:93)
您的异步测试会在失败的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 );
}
}
答案 1 :(得分:19)
以下是我对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')
}))
})
答案 2 :(得分:13)
如果您愿意答应,请尝试Chai as Promised + Q,其中包含以下内容:
doSomethingAsync().should.eventually.equal("foo").notify(done);
答案 3 :(得分:2)
我在Mocha邮件列表中问了同样的事情。他们基本上告诉我这个:用Mocha和Chai编写异步测试:
if (err) done(err);
done()
结束测试。 它解决了我的问题,并没有更改我的代码中间的一行(Chai期望其他)。 setTimout
不是进行异步测试的方法。
答案 4 :(得分:1)
我发布了解决此问题的软件包。
首先安装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 Is there a way to get Chai working with asynchronous Mocha tests?我将其作为NPM包发布。
有关详细信息,请参阅https://github.com/niftylettuce/check-chai。
答案 5 :(得分:1)
尝试chaiAsPromised!除了出色的命名之外,您还可以使用以下语句:
expect(asyncToResultingValue()).to.eventually.equal(true)
Can confirm,适用于Mocha + Chai。
答案 6 :(得分:1)
与Jean Vincent's answer非常相关并受其启发,我们使用类似于check
函数的辅助函数,但我们将其称为eventually
(这有助于它与命名相匹配) chai-as-promise的惯例。它返回一个函数,该函数接受任意数量的参数并将它们传递给原始回调。这有助于消除测试中额外的嵌套功能块,并允许您处理任何类型的异步回调。这里写的是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");
});
});
答案 7 :(得分:1)
我知道有很多重复的答案和建议的解决方案,但是我没有看到上面的简单解决方案为这两种用例提供简洁的模式。我将其作为合并答案发布给其他希望复制粘贴的人:
function expectEventCallback(done, fn) {
return function() {
try { fn(...arguments); }
catch(error) { return done(error); }
done();
};
}
function expectNodeCallback(done, fn) {
return function(err, ...args) {
if (err) { return done(err); }
try { fn(...args); }
catch(error) { return done(error); }
done();
};
}
it('handles event callbacks', function(done) {
something.on('event', expectEventCallback(done, (payload) => {
expect(payload).to.have.propertry('foo');
}));
});
it('handles node callbacks', function(done) {
doSomething(expectNodeCallback(done, (payload) => {
expect(payload).to.have.propertry('foo');
}));
});
答案 8 :(得分:0)
我解决了将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);
});
});
它也可以调试。
答案 9 :(得分:0)
根据@richardforrester http://staxmanade.com/2015/11/testing-asyncronous-code-with-mochajs-and-es7-async-await/提供的链接,如果省略done参数,describe可以使用返回的Promise。
只有缺点是必须有一个Promise,而不是任何异步函数(你可以用Promise包装它,你)。但在这种情况下,代码可以大大减少。
它考虑了初始funcThatReturnsAPromise函数或期望值中的任何一个失败:
it('should test Promises', function () { // <= done removed
return testee.funcThatReturnsAPromise({'name': 'value'}) // <= return added
.then(response => expect(response).to.have.property('ok', 1));
});
答案 10 :(得分:0)
测试和异步过程中的计时器听起来很粗糙。有一种方法可以使用基于承诺的方法来实现。
const sendFormResp = async (obj) => {
const result = await web.chat.postMessage({
text: 'Hello world!',
});
return result
}
此异步功能使用Web客户端(在本例中为Slacks SDK)。 SDK负责API调用的异步性质,并返回有效负载。然后,我们可以针对异步承诺中返回的对象运行expect
,从而在chai中测试有效负载。
describe("Slack Logic For Working Demo Environment", function (done) {
it("Should return an object", () => {
return sdkLogic.sendFormResp(testModels.workingModel).then(res => {
expect(res).to.be.a("Object");
})
})
});
答案 11 :(得分:0)
更简单的方法是使用 wait-for-expect 库。
const waitForExpect = require("wait-for-expect")
test("it waits for the number to change", async () => {
let numberToChange = 10;
setTimeout(() => {
numberToChange = 100;
}, randomTimeout);
await waitForExpect(() => {
expect(numberToChange).toEqual(100);
});
});
答案 12 :(得分:-2)
对我来说非常有效icm Mocha / Chai是来自Sinon图书馆的假人。 只需在测试中推进计时器。
var sinon = require('sinon');
clock = sinon.useFakeTimers();
// Do whatever.
clock.tick( 30000 ); // Advances the JS clock 30 seconds.
让测试更快完成的额外好处。
答案 13 :(得分:-2)
您还可以使用域模块。例如:
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
});