簡體   English   中英

使用 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”

(節點:25754)UnhandledPromiseRejectionWarning:未處理的承諾拒絕(拒絕ID:2):AssertionError:承諾錯誤:預期{Object(message,showDiff,...)}為假

  1. 應該轉換為正確的事件

然后,2秒后我得到

錯誤:超過 2000 毫秒的超時。 確保在此測試中調用 done() 回調。

這更奇怪,因為執行了 catch 回調(我認為由於某種原因,斷言失敗阻止了其余的執行)

現在有趣的是,如果我注釋掉assert.isNotOk(error...)測試運行良好,控制台中沒有任何警告。 從它執行捕獲的意義上說,它仍然“失敗”。
但是,我仍然無法通過承諾來理解這些錯誤。 有人可以啟發我嗎?

問題是由以下原因引起的:

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

如果斷言失敗,它將拋出錯誤。 這個錯誤會導致done()永遠不會被調用,因為代碼在它之前出錯了。 這就是超時的原因。

“未處理的承諾拒絕”也是由失敗的斷言引起的,因為如果在catch()處理程序中拋出錯誤,並且沒有后續的catch()處理程序,錯誤將被吞噬(如本文所述) )。 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');
  });
});

我在用 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

如果斷言不正確,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 中測試 promise 時使用 .then(done,done) 的想法。

對於那些在測試環境之外尋找錯誤/警告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 */ });

我遇到了這個問題:

(node:1131004) UnhandledPromiseRejectionWarning: 未處理的承諾拒絕 (rejection id: 1): TypeError: res.json is not a function (node:1131004) DeprecationWarning: 未處理的承諾拒絕被棄用。 將來,未處理的承諾拒絕將以非零退出代碼終止 Node.js 進程。

這是我的錯誤,我在then(function(res)替換了res對象,因此將res更改為 result ,現在它可以工作了。

錯誤的

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
}

這是我使用E7 async/await 的經驗:

如果你有一個async helperFunction()從你的測試中調用......(一個帶有 ES7 async關鍵字的明確,我的意思是)

→ 請確保,您將其稱為await helperFunction(whateverParams) (嗯,是的,自然,一旦您知道...)

為了使其工作(為了避免“等待是保留字”),您的測試函數必須有一個外部異步標記:

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

卸載webpack后我解決了這個問題(反應js問題)。

sudo uninstall webpack

我對 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)
        })
      })

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM