简体   繁体   English

使用 mocha/chai 进行测试时获得 UnhandledPromiseRejectionWarning

[英]Getting a UnhandledPromiseRejectionWarning when testing using mocha/chai

So, I'm testing a component that relies on an event-emitter.所以,我正在测试一个依赖于事件发射器的组件。 To do so I came up with a solution using Promises with Mocha+Chai:为此,我想出了一个使用 Promises 和 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();
  });
});

On the console I'm getting an 'UnhandledPromiseRejectionWarning' even though the reject function is getting called since it instantly shows the message 'AssertionError: Promise error'在控制台上,即使调用了拒绝函数,我也会收到“UnhandledPromiseRejectionWarning”,因为它立即显示消息“AssertionError: Promise error”

(node:25754) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): AssertionError: Promise error: expected { Object (message, showDiff, ...) } to be falsy (节点:25754)UnhandledPromiseRejectionWarning:未处理的承诺拒绝(拒绝ID:2):AssertionError:承诺错误:预期{Object(message,showDiff,...)}为假

  1. should transition with the correct event应该转换为正确的事件

And then, after 2 sec I get然后,2秒后我得到

Error: timeout of 2000ms exceeded.错误:超过 2000 毫秒的超时。 Ensure the done() callback is being called in this test.确保在此测试中调用 done() 回调。

Which is even weirder since the catch callback was executed(I think that for some reason the assert failure prevented the rest of the execution)这更奇怪,因为执行了 catch 回调(我认为由于某种原因,断言失败阻止了其余的执行)

Now the funny thing, if I comment out the assert.isNotOk(error...) the test runs fine without any warning in the console.现在有趣的是,如果我注释掉assert.isNotOk(error...)测试运行良好,控制台中没有任何警告。 It stills 'fails' in the sense that it executes the catch.从它执行捕获的意义上说,它仍然“失败”。
But still, I can't understand these errors with promise.但是,我仍然无法通过承诺来理解这些错误。 Can someone enlighten me?有人可以启发我吗?

The issue is caused by this:问题是由以下原因引起的:

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

If the assertion fails, it will throw an error.如果断言失败,它将抛出错误。 This error will cause done() never to get called, because the code errored out before it.这个错误会导致done()永远不会被调用,因为代码在它之前出错了。 That's what causes the timeout.这就是超时的原因。

The "Unhandled promise rejection" is also caused by the failed assertion, because if an error is thrown in a catch() handler, and there isn't a subsequent catch() handler , the error will get swallowed (as explained in this article ). “未处理的承诺拒绝”也是由失败的断言引起的,因为如果在catch()处理程序中抛出错误,并且没有后续的catch()处理程序,错误将被吞噬(如本文所述) )。 The UnhandledPromiseRejectionWarning warning is alerting you to this fact. UnhandledPromiseRejectionWarning警告提醒您注意这一事实。

In general, if you want to test promise-based code in Mocha, you should rely on the fact that Mocha itself can handle promises already.一般来说,如果你想在 Mocha 中测试基于 promise 的代码,你应该依赖 Mocha 本身已经可以处理 promise 的事实。 You shouldn't use done() , but instead, return a promise from your test.你不应该使用done() ,而是从你的测试中返回一个承诺。 Mocha will then catch any errors itself. Mocha 将自己捕获任何错误。

Like this:像这样:

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

I got this error when stubbing with sinon.我在用 sinon 存根时遇到了这个错误。

The fix is to use npm package sinon-as-promised when resolving or rejecting promises with stubs.解决方法是在使用存根解决或拒绝承诺时使用 npm 包sinon-as-promised

Instead of ...代替 ...

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

Use ...用 ...

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

There is also a resolves method (note the s on the end).还有一个 resolves 方法(注意末尾的 s)。

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

The assertion libraries in Mocha work by throwing an error if the assertion was not correct.如果断言不正确,Mocha 中的断言库通过抛出错误来工作。 Throwing an error results in a rejected promise, even when thrown in the executor function provided to the catch method.抛出错误会导致被拒绝的承诺,即使在提供给catch方法的执行程序函数中抛出也是如此。

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

In the above code the error objected evaluates to true so the assertion library throws an error... which is never caught.在上面的代码中,被反对的error评估为true所以断言库抛出一个错误......永远不会被捕获。 As a result of the error the done method is never called.由于错误, done方法永远不会被调用。 Mocha's done callback accepts these errors, so you can simply end all promise chains in Mocha with .then(done,done) . Mocha 的done回调接受这些错误,因此您可以简单地使用.then(done,done)结束 Mocha 中的所有承诺链。 This ensures that the done method is always called and the error would be reported the same way as when Mocha catches the assertion's error in synchronous code.这确保了 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);
});

I give credit to this article for the idea of using .then(done,done) when testing promises in Mocha.我将这篇文章归功于在 Mocha 中测试 promise 时使用 .then(done,done) 的想法。

For those who are looking for the error/warning UnhandledPromiseRejectionWarning outside of a testing environment, It could be probably because nobody in the code is taking care of the eventual error in a promise:对于那些在测试环境之外寻找错误/警告UnhandledPromiseRejectionWarning的人来说,这可能是因为代码中没有人处理承诺中的最终错误:

For instance, this code will show the warning reported in this question:例如,此代码将显示此问题中报告的警告:

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

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

and adding the .catch() or handling the error should solve the warning/error并添加.catch()或处理错误应该解决警告/错误

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

Or using the second parameter in the then function或者在then函数中使用第二个参数

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

I faced this issue:我遇到了这个问题:

(node:1131004) UnhandledPromiseRejectionWarning: Unhandled promise rejection (re jection id: 1): TypeError: res.json is not a function (node:1131004) DeprecationWarning: Unhandled promise rejections are deprecated. (node:1131004) UnhandledPromiseRejectionWarning: 未处理的承诺拒绝 (rejection id: 1): TypeError: res.json is not a function (node:1131004) DeprecationWarning: 未处理的承诺拒绝被弃用。 In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.将来,未处理的承诺拒绝将以非零退出代码终止 Node.js 进程。

It was my mistake, I was replacing res object in then(function(res) , so changed res to result and now it is working.这是我的错误,我在then(function(res)替换了res对象,因此将res更改为 result ,现在它可以工作了。

Wrong错误的

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

Correction更正

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

Service code:服务代码:

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
}

Here's my take experience with E7 async/await :这是我使用E7 async/await 的经验:

In case you have an async helperFunction() called from your test... (one explicilty with the ES7 async keyword, I mean)如果你有一个async helperFunction()从你的测试中调用......(一个带有 ES7 async关键字的明确,我的意思是)

→ make sure, you call that as await helperFunction(whateverParams) (well, yeah, naturally, once you know...) → 请确保,您将其称为await helperFunction(whateverParams) (嗯,是的,自然,一旦您知道...)

And for that to work (to avoid 'await is a reserved word'), your test-function must have an outer async marker:为了使其工作(为了避免“等待是保留字”),您的测试函数必须有一个外部异步标记:

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

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

sudo uninstall webpack

I had a similar experience with Chai-Webdriver for Selenium.我对 Selenium 的 Chai-Webdriver 也有类似的经验。 I added await to the assertion and it fixed the issue:我在断言中添加了await并解决了这个问题:

Example using Cucumberjs:使用 Cucumberjs 的示例:

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

Just a heads-up that you can get a UnhandledPromiseRejectionWarning if you accidentally put your test code outside of the it-function.请注意,如果您不小心将测试代码放在 it 函数之外,您可能会收到UnhandledPromiseRejectionWarning 😬 😬

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM