NodeJS UnhandledPromiseRejectionWarning

时间:2016-09-27 05:23:52

标签: javascript node.js promise mocha chai

所以,我正在测试一个依赖于事件发射器的组件。为此,我想出了一个使用Promise with Mocha + Chai的解决方案:

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();
    });
  });
});

在控制台上,我得到了一个未处理的提示撤消警告'即使拒绝函数被调用,因为它立即显示消息' AssertionError:Promise error'

  

(node:25754)UnhandledPromiseRejectionWarning:未处理的承诺   rejection(拒绝id:2):AssertionError:Promise错误:预期   {Object(message,showDiff,...)}是假的   1)应该使用正确的事件进行转换

然后,在2秒后我得到了

  

错误:超过2000毫秒的超时。确保done()回调是   在这次考试中被召唤。

自执行catch回调以来,这甚至更奇怪。(我认为由于某种原因,断言失败阻止了其余的执行)

现在有趣的是,如果我注释掉assert.isNotOk(error...),测试运行正常,控制台中没有任何警告。它仍然失败'从它执行捕获的意义上来说 但是,我仍然无法理解这些错误。有人可以开导我吗?

9 个答案:

答案 0 :(得分:130)

问题是由此造成的:

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

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

"未处理的承诺拒绝" 也是由失败的断言引起的,因为如果catch()处理程序中出现错误,并且没有& #39;随后的catch()处理程序,错误将被吞噬(如this article中所述)。 UnhandledPromiseRejectionWarning警告提醒您注意这一事实。

一般来说,如果你想在Mocha中测试基于promise的代码,你应该依赖Mocha本身可以处理promise的事实。你不应该使用done(),而是从你的测试中返回一个承诺。然后Mocha将自己捕获任何错误。

像这样:

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');
  });
});

答案 1 :(得分:9)

当与sinon衔接时我得到了这个错误。

修复是在使用存根解析或拒绝承诺时使用npm包 sinon-as-promised

而不是......

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

使用......

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

还有一种解决方法(注意结尾处的s)。

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

答案 2 :(得分:8)

如果断言不正确,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);
});

我赞赏this article在Mocha中测试承诺时使用.then(done,done)的想法。

答案 3 :(得分:4)

对于那些在测试环境之外寻找错误/警告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 */ });

答案 4 :(得分:1)

以下是我对E7 async/await

的体验

如果您的测试中调用了async helperFunction() ...(我的意思是ES7 async关键字的一个明确的说明)

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

为了实现这一点(为了避免'await是一个保留字'),你的测试函数必须有一个外部异步标记:

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

答案 5 :(得分:0)

我遇到了这个问题:

  

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

这是我的错误,我正在替换/中的res对象,因此将then(function(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");
              });

答案 6 :(得分:0)

我在卸载webpack后解决了这个问题(js问题)。

sudo uninstall webpack

答案 7 :(得分:0)

我在Cele-Webdriver for Selenium上也有类似的经历。 我在断言中添加了await,并解决了该问题:

使用Cucumberjs的示例:

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

答案 8 :(得分:0)

请注意,如果您不小心将测试代码放在 it 函数之外,您可能会得到 UnhandledPromiseRejectionWarning。 ?

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