使用 mocha/chai 进行测试时出现 UnhandledPromiseRejectionWarning

Getting a UnhandledPromiseRejectionWarning when testing using 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();
  });
});

在控制台上,我得到了一个 'UnhandledPromiseRejectionWarning',尽管正在调用拒绝函数,因为它会立即显示消息 'AssertionError: Promise error'

(node:25754) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): AssertionError: Promise error: expected { Object (message, showDiff, ...) } to be falsy

  1. should transition with the correct event

然后,2 秒后我得到

Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.

自从执行了 catch 回调之后就更奇怪了(我认为由于某种原因断言失败阻止了其余的执行)

现在有趣的是,如果我注释掉 assert.isNotOk(error...) 测试运行正常,控制台中没有任何警告。它在执行捕获的意义上静止'fails'。
但是,我仍然无法理解这些错误。谁能赐教一下?

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

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

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

"Unhandled promise rejection" 也是由失败的断言引起的,因为如果在 catch() 处理程序中抛出错误, and没有后续的 catch() 处理程序 ,错误将被吞没(如 this article 中所述)。 UnhandledPromiseRejectionWarning 警告提醒您注意这一事实。

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

像这样:

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-as-promised

而不是...

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

我遇到了这个问题:

(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. In the future, promise rejections that are not handled will terminate the Node.j s process with a non-zero exit code.

这是我的错误,我正在替换 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 objected 的计算结果为 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 中测试 promises 时使用 .then(done,done) 的想法。

这是我对 E7 async/await 的体验:

万一你有一个 async helperFunction() 从你的测试中调用......(我的意思是 ES7 async 关键字的一个明确)

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

为了让它起作用(为了避免“await 是一个保留字”),你的测试函数必须有一个外部异步标记:

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

对于那些在测试环境之外寻找 error/warning UnhandledPromiseRejectionWarning 的人,这可能是因为代码中没有人处理承诺中的最终错误:

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

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

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

并添加 .catch() 或处理错误应该可以解决 warning/error

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-Webdriver 有类似的体验。 我在断言中添加了 await 并解决了问题:

使用 Cucumberjs 的示例:

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

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

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