Получение UnhandledPromiseRejectionWarning при тестировании с использованием мокко / чай

Итак, я тестирую компонент, который полагается на эмиттер событий. Для этого я придумал решение, используя 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: ошибка обещания».

(узел: 25754) UnhandledPromiseRejectionWarning: необработанное отклонение обещания (идентификатор отклонения: 2): AssertionError: ошибка обещания: ожидается, что {Object (message, showDiff, ...)} будет ложным

  1. должен перейти с правильным событием

А потом, через 2 секунды, я получаю

Ошибка: превышено время ожидания 2000 мс. Убедитесь, что в этом тесте вызывается обратный вызов done ().

Что еще более странно, поскольку был выполнен обратный вызов catch (я думаю, что по какой-то причине сбой assert помешал остальной части выполнения)

А теперь самое забавное: если я закомментирую assert.isNotOk(error...), тест пройдет нормально без каких-либо предупреждений в консоли. Он по-прежнему «не работает» в том смысле, что выполняет уловку.
Но все же я не могу понять эти ошибки с помощью обещания. Может кто меня просветить?


person Jzop    schedule 27.09.2016    source источник
comment
Я думаю, у вас есть еще один дополнительный набор закрывающих скобок и скобок в самой последней строке. Пожалуйста, удалите их и попробуйте еще раз.   -  person Redu    schedule 27.09.2016
comment
Это так круто, новое предупреждение о необработанном отказе обнаруживает ошибки в реальной жизни и экономит время людей. Здесь столько выигрыша. Без этого предупреждения время ожидания ваших тестов истекло без каких-либо объяснений.   -  person Benjamin Gruenbaum    schedule 27.09.2016


Ответы (8)


Проблема вызвана следующим:

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

Если утверждение терпит неудачу, оно выдаст ошибку. Эта ошибка приведет к тому, что done() никогда не будет вызван, потому что код был ошибочным до этого. Вот что вызывает тайм-аут.

«Отклонение необработанного обещания» также вызвано неудавшимся подтверждением, потому что, если ошибка возникает в catch() обработчике, и нет последующего catch() обработчика, ошибка будет проглочена (как описано в этой статье). Предупреждение UnhandledPromiseRejectionWarning предупреждает вас об этом факте.

В общем, если вы хотите протестировать код, основанный на обещаниях, в Mocha, вы должны полагаться на тот факт, что сам Mocha уже может обрабатывать обещания. Вы не должны использовать 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');
  });
});
person robertklep    schedule 27.09.2016
comment
Для всех, кому интересно, это верно и для жасмина. - person Nick Radford; 07.03.2017
comment
@robertklep Не поймаешь, вызовут ли любую ошибку, а не только ту, которую вы ожидаете? Я думаю, что этот стиль не сработает, если вы пытаетесь констатировать неудачи. - person TheCrazyProgrammer; 08.06.2017
comment
@TheCrazyProgrammer обработчик catch, вероятно, должен быть передан в качестве второго аргумента в then. Однако я не полностью уверен, в чем заключалась цель OP, поэтому я оставил все как есть. - person robertklep; 08.06.2017
comment
А также всем, кто интересуется жасмином, в этом случае используйте done.fail('msg'). - person Paweł; 30.08.2017
comment
Можете ли вы привести полный пример того, где должен идти основной код и утверждения, здесь не так ясно ..... особенно если вы утверждаете значение из исходного вызова службы / обещания в нашем коде. Соответствует ли реальное обещание нашего кода этому другому обещанию мокко? - person bjm88; 07.11.2018
comment
@ bjm88, возможно, поможет, если вы рассмотрите свой тестовый пример как .then обработчик: вы возвращаете из него цепочку обещаний, и обычно эта цепочка начинается с тестируемой функции, за которой следует .then, где вы утверждаете, что возвращенное значение (я) верны. См. эту суть. - person robertklep; 07.11.2018
comment
Чтобы использовать поведение по умолчанию, предоставляемое mocha, просто верните обещание без catch. Он пометит тест как неудачный, если обещание будет отклонено, и распечатает ошибку. - person Riley Steele Parsons; 17.08.2019

Я получил эту ошибку при заглушке с синоном.

Исправление состоит в том, чтобы использовать пакет npm sinon-as -asted при разрешении или отклонении обещаний с заглушками.

Вместо того ...

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-rejection

person danday74    schedule 21.10.2016
comment
Sinon теперь включает методы разрешает и отклоняет заглушки, начиная с версии 2. См. npmjs.com/ пакет / синон-как-обещал. Я все же поставил +1 к ответу - я не знал об этом. - person Andrew; 20.05.2018

Библиотеки утверждений в Mocha работают, выкидывая ошибку, если утверждение было неверным. Выдача ошибки приводит к отклонению обещания, даже если оно выбрано функцией исполнителя, предоставленной методу catch.

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

В приведенном выше коде объект error оценивается как true, поэтому библиотека утверждений выдает ошибку ... которая никогда не перехватывается. В результате ошибки метод done никогда не вызывается. Обратный вызов Mocha done принимает эти ошибки, поэтому вы можете просто завершить все цепочки обещаний в Mocha с помощью .then(done,done). Это гарантирует, что метод 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);
});

Я отдаю должное этой статье за идею использования .then (готово , готово) при тестировании обещаний в Mocha.

person Matthew Orlando    schedule 09.06.2017

Для тех, кто ищет ошибку / предупреждение 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 */ });
person gsalgadotoledo    schedule 05.04.2018
comment
Конечно, но я думаю, что в реальной жизни мы обычно используем не просто new Promise((resolve, reject) => { return reject('Error reason!'); }), а в функции function test() { return new Promise((resolve, reject) => { return reject('Error reason!'); });}, поэтому внутри функции нам не нужно использовать .catch(), но для успешной обработки ошибок ее достаточно использовать при вызове этой функции test().catch(e => console.log(e)) или версии async / await. try { await test() } catch (e) { console.log(e) } - person mikep; 28.01.2019

Я столкнулся с этой проблемой:

(node: 1131004) UnhandledPromiseRejectionWarning: отклонение необработанного обещания (идентификатор отклонения: 1): TypeError: res.json не является функцией (node: 1131004) DeprecationWarning: необработанные отклонения обещаний устарели. В будущем необработанные отклонения обещаний завершат процесс 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");
              });

Сервисный код:

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
}
person Muhammad Shahzad    schedule 03.05.2017

Вот мой опыт работы с E7 async / await:

Если у вас есть async helperFunction(), вызванный из вашего теста ... (я имею в виду, одно явно с ключевым словом ES7 async)

→ убедитесь, что вы называете это как await helperFunction(whateverParams) (ну да, естественно, как только вы узнаете ...)

И для того, чтобы это сработало (чтобы избежать «ожидание - это зарезервированное слово»), ваша тестовая функция должна иметь внешний асинхронный маркер:

it('my test', async () => { ...
person Frank Nocke    schedule 30.10.2017
comment
У вас нет необходимости называть его await helperFunction(...). Функция async возвращает обещание. Вы можете просто обработать возвращенное обещание, как если бы вы поступили с функцией, не отмеченной async, которая возвращает обещание. Дело в том, чтобы выполнить обещание, и точка. Независимо от того, является ли функция async или нет. await - это всего лишь один из множества способов выполнить обещание. - person Louis; 30.10.2017
comment
Правда. Но тогда я должен вложить деньги в отлов ... или мои тесты пройдут как ложные срабатывания, и любые невыполненные обещания останутся незамеченными (с точки зрения результатов тестировщика). Так что для меня ожидание выглядит как меньше строк и усилий. - Во всяком случае, это было причиной того UnhandledPromiseRejectionWarning для меня, что я забыл об ожидании ... таким образом, этот ответ. - person Frank Nocke; 31.10.2017

У меня был аналогичный опыт с Chai-Webdriver для Selenium. Я добавил await к утверждению, и проблема была устранена:

Пример использования Cucumberjs:

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

Просто предупреждаю, что вы можете получить UnhandledPromiseRejectionWarning, если случайно поместите свой тестовый код за пределы it-функции. ????

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