在使用摩卡/印度茶进行测试时收到未处理的承诺拒绝警告

我在测试一个依赖于事件发射器的组件。为了做到这一点,我想出了一个解决方案,使用“摩卡 + 印度茶的承诺”:

it('should transition with the correct event', (done) => {
const cFSM = new CharacterFSM({}, emitter, transitions);
let timeout = null;
let resolved = false;
new Promise((resolve, reject) => {
emitter.once('action', resolve);
emitter.emit('done', {});
timeout = setTimeout(() => {
if (!resolved) {
reject('Timedout!');
}
clearTimeout(timeout);
}, 100);
}).then((state) => {
resolved = true;
assert(state.action === 'DONE', 'should change state');
done();
}).catch((error) => {
assert.isNotOk(error,'Promise error');
done();
});
});

在控制台上,我得到了一个‘ Unhandled 种承诺拒绝警告’,尽管拒绝函数被调用,因为它立即显示消息‘ AssertionError:  承诺错误’

(node: 25754)未处理的承诺拒绝警告: 未处理的承诺 拒绝(拒绝 id: 2) : 断言错误: 承诺错误: 预期 { Object (message,showDiff,...)}为 false

  1. 应该与正确的事件转换

然后,两秒钟之后

错误: 超时2000ms。请确保 done ()回调为 在这次测试中被召唤。

由于 catch 回调被执行(我认为由于某种原因断言失败阻止了其余的执行) ,这种情况更加奇怪

有趣的是,如果我注释掉 assert.isNotOk(error...),测试在控制台没有任何警告的情况下运行得很好。在执行 catch 的意义上,它仍然“失败”。
但是,我仍然不能理解这些错误与承诺。有人可以启发我吗?

520814 次浏览

这个问题是由以下原因引起的:

.catch((error) => {
assert.isNotOk(error,'Promise error');
done();
});

如果断言失败,它将抛出一个错误。此错误将导致 done()永远不会被调用,因为代码在此之前出错。这就是暂停的原因。

“未经处理的拒绝承诺”也是由失败的断言引起的,因为如果在 catch()处理程序 没有后续的 catch()处理程序中抛出错误,错误将被吞噬(如 这篇文章中所解释的)。UnhandledPromiseRejectionWarning警告提醒您注意这个事实。

通常,如果您想在 Mocha 中测试基于承诺的代码,那么您应该依赖于 Mocha 本身已经可以处理承诺的事实。您不应该使用 done(),而应该从测试返回一个承诺。然后摩卡会自己捕捉任何错误。

像这样:

it('should transition with the correct event', () => {
...
return new Promise((resolve, reject) => {
...
}).then((state) => {
assert(state.action === 'DONE', 'should change state');
})
.catch((error) => {
assert.isNotOk(error,'Promise error');
});
});

我和 Sinon 撞车时犯了这个错误。

修复方法是在解析或拒绝使用存根的承诺时使用 npm 包 如约而至

而不是..。

sinon.stub(Database, 'connect').returns(Promise.reject( Error('oops') ))

用..。

require('sinon-as-promised');
sinon.stub(Database, 'connect').rejects(Error('oops'));

还有一个 Resolves 方法(请注意末尾的 s)。

参见 http://clarkdave.net/2016/09/node-v6-6-and-asynchronously-handled-promise-rejections

我遇到了这个问题:

(节点: 1131004)未处理的承诺拒绝警告: 未处理的承诺拒绝(re 注释 id: 1) : TypeError: res.json 不是一个函数 (节点: 1131004)弃权警告: 不推荐使用未处理的承诺拒绝。 将来,未处理的承诺拒绝将终止 Node.j S 进程的非零退出代码。

这是我的错误,我正在取代 then(function(res)中的 res对象,所以改变了 res的结果,现在它正在工作。

错了

module.exports.update = function(req, res){
return Services.User.update(req.body)
.then(function(res){//issue was here, res overwrite
return res.json(res);
}, function(error){
return res.json({error:error.message});
}).catch(function () {
console.log("Promise Rejected");
});

纠正一下

module.exports.update = function(req, res){
return Services.User.update(req.body)
.then(function(result){//res replaced with result
return res.json(result);
}, function(error){
return res.json({error:error.message});
}).catch(function () {
console.log("Promise Rejected");
});

服务编号:

function update(data){
var id = new require('mongodb').ObjectID(data._id);
userData = {
name:data.name,
email:data.email,
phone: data.phone
};
return collection.findAndModify(
{_id:id}, // query
[['_id','asc']],  // sort order
{$set: userData}, // replacement
{ "new": true }
).then(function(doc) {
if(!doc)
throw new Error('Record not updated.');
return doc.value;
});
}


module.exports = {
update:update
}

Mocha 中的断言库通过在断言不正确时抛出错误来工作。抛出错误会导致承诺被拒绝,即使在抛出提供给 catch方法的执行函数时也是如此。

.catch((error) => {
assert.isNotOk(error,'Promise error');
done();
});

在上面的代码中,error对象的计算结果为 true,因此断言库会抛出一个错误... 这个错误永远不会被捕获。由于该错误,从不调用 done方法。Mocha 的 done回调接受这些错误,因此您可以简单地用 .then(done,done)结束 Mocha 中的所有承诺链。这确保了 done 方法总是被调用,并且错误的报告方式与 Mocha 在同步代码中捕获断言错误的方式相同。

it('should transition with the correct event', (done) => {
const cFSM = new CharacterFSM({}, emitter, transitions);
let timeout = null;
let resolved = false;
new Promise((resolve, reject) => {
emitter.once('action', resolve);
emitter.emit('done', {});
timeout = setTimeout(() => {
if (!resolved) {
reject('Timedout!');
}
clearTimeout(timeout);
}, 100);
}).then(((state) => {
resolved = true;
assert(state.action === 'DONE', 'should change state');
})).then(done,done);
});

在 Mocha 中测试承诺时,我将使用. then (done,done)的想法归功于 这篇文章

以下是我对 E7异步/等待的体验:

如果您有一个 async helperFunction()从您的测试调用... (一个明确的 ES7async关键字,我的意思是)

→确保你称之为 await helperFunction(whateverParams)(好吧,是的,当然,一旦你知道...)

为了达到这个目的(为了避免‘ wait 是一个保留字’) ,你的测试函数必须有一个外部的异步标记:

it('my test', async () => { ...

对于那些在测试环境之外寻找错误/警告 UnhandledPromiseRejectionWarning的人来说,这可能是因为代码中没有人关心承诺中的最终错误:

例如,此代码将显示此问题中报告的警告:

new Promise((resolve, reject) => {
return reject('Error reason!');
});

(node:XXXX) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Error reason!

并添加 .catch()或处理错误应解决警告/错误

new Promise((resolve, reject) => {
return reject('Error reason!');
}).catch(() => { /* do whatever you want here */ });

或者使用 then函数中的第二个参数

new Promise((resolve, reject) => {
return reject('Error reason!');
}).then(null, () => { /* do whatever you want here */ });

我也有过类似的经历,就是在 Selenium 的 chai- 网络驱动程序上。 我在断言中加入了 await,它解决了这个问题:

使用 Cucumberjs 的例子:

Then(/I see heading with the text of Tasks/, async function() {
await chai.expect('h1').dom.to.contain.text('Tasks');
});

只是提醒一下,如果您不小心将测试代码放在 it-function 之外,您可能会得到一个 UnhandledPromiseRejectionWarning

    describe('My Test', () => {
context('My Context', () => {
it('should test something', () => {})
const result = testSomething()
assert.isOk(result)
})
})