node.js 如何使用 mocha 和 chai 正确测试 promise?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26571328/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I properly test promises with mocha and chai?
提问by chovy
The following test is behaving oddly:
以下测试行为异常:
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);
})
});
How should I properly handle a rejected promise (and test it)?
我应该如何正确处理被拒绝的承诺(并测试它)?
How should I properly handle a failed test (ie: expect(data.rate).to.have.length(400);?
我应该如何正确处理失败的测试(即:expect(data.rate).to.have.length(400);?
Here is the implementation I'm testing:
这是我正在测试的实现:
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
});
};
回答by Benjamin Gruenbaum
The easiest thing to do would be to use the built in promises support Mocha has in recent versions:
最简单的方法是使用 Mocha 在最近的版本中内置的 Promise 支持:
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
});
Or with modern Node and async/await:
或者使用现代 Node 和 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);
});
Since this approach is promises end to end it is easier to test and you won't have to think about the strange cases you're thinking about like the odd done()calls everywhere.
由于这种方法是端到端的承诺,因此更容易测试,并且您不必考虑您正在考虑的奇怪情况,例如done()到处都是奇怪的电话。
This is an advantage Mocha has over other libraries like Jasmine at the moment. You might also want to check Chai As Promisedwhich would make it even easier (no .then) but personally I prefer the clarity and simplicity of the current version
这是 Mocha 目前相对于其他库(如 Jasmine)的优势。您可能还想查看Chai As Promised,这会使它更容易(否.then),但我个人更喜欢当前版本的清晰和简单
回答by fearless_fool
As already pointed out here, the newer versions of Mocha are already Promise-aware. But since the OP asked specifically about Chai, it's only fair to point out the chai-as-promisedpackage which provides a clean syntax for testing promises:
正如这里已经指出的,新版本的 Mocha 已经支持 Promise。但是由于 OP 专门询问了 Chai,因此指出chai-as-promised提供用于测试承诺的干净语法的包是公平的:
using chai-as-promised
使用 chai-as-promise
Here's how you can use chai-as-promised to test both resolveand rejectcases for a Promise:
以下是如何使用 chai-as-promised 来测试Promiseresolve和rejectcase 的方法:
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');
});
without chai-as-promised
没有chai-as-promise
To make it really clear as to what's getting tested, here's the same example coded without chai-as-promised:
为了真正清楚地了解正在测试的内容,这里是没有 chai-as-promised 编码的相同示例:
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'); })
;
});
回答by Frank Nocke
Here's my take:
这是我的看法:
- using
async/await - not needing extra chai modules
- avoiding the catch issue, @TheCrazyProgrammer pointed out above
- 使用
async/await - 不需要额外的柴模块
- 避免捕获问题,@TheCrazyProgrammer 上面指出
A delayed promise function, that fails, if given a delay of 0:
延迟承诺函数,如果延迟为 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')
})
Positive testis rather simple. Unexpected failure (simulate by 500→0) will fail the test automatically, as rejected promise escalates.
阳性测试相当简单。意外失败(由 模拟500→0)将自动使测试失败,因为被拒绝的承诺会升级。
Negative testuses the try-catch-idea. However: 'complaining' about an undesired pass happens only after the catch clause (that way, it does not end up in the catch() clause, triggering further but misleading errors.
否定测试使用 try-catch-idea。但是:“抱怨”不希望的传递只发生在 catch 子句之后(这样,它不会在 catch() 子句中结束,从而进一步触发但具有误导性的错误。
For this strategy to work, one must return the test from the catch clause. If you want't to test anything else, use another it()-block.
要使这一策略起作用,必须从 catch 子句返回测试。如果您不想测试任何其他内容,请使用另一个 it() 块。
回答by di3
Thre is a better solution. Just return the error with done in a catch block.
有一个更好的解决方案。只需在 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
});
});
this test will fail with following message: AssertionError: expected 1 to equal 11
此测试将失败并显示以下消息: AssertionError: expected 1 to equal 11

