Mocha: Асинхронный тест завершается неудачно с тайм-аутом вместо ошибки утверждения

Созданный на 5 февр. 2014  ·  25Комментарии  ·  Источник: mochajs/mocha

Этот тест:

    it('returns the correct value', function(done) {
      var returnValue = 5;

      aPromise.then(function() {
        expect(returnValue).to.equal(42);
        done();
      });

    });

Этот тест не проходит с ошибкой утверждения timeout of 2000ms exceeded . Я предполагаю, что это потому, что вызов expect() вызывает ошибку, а done() никогда не выполняется, и мне интересно, есть ли лучший способ протестировать такой код.

Самый полезный комментарий

Я столкнулся с аналогичной проблемой и в конце концов понял, что вы не должны использовать done при тестировании асинхронных функций с обещаниями, вместо этого просто верните обещание. Таким образом, вы сможете сделать это без тайм-аута, например:

it('returns the correct value', function() {
    var returnValue = 5;

    return aPromise.then(function() {
        expect(returnValue).to.equal(42);
    });
});

Все 25 Комментарий

aPromise когда-нибудь разрешается? В противном случае нет другого выбора, кроме как сбросить тайм-аут.

@NickHeiner Да, решает; а затем expect() находит, что returnValue не является equal(42) и выбрасывает.

@gurdiga, откуда вы знаете, что он выдает, если вы получаете тайм-аут, а не ошибку утверждения?

@hallas @NickHeiner Вот работающая штука: http://jsfiddle.net/gurdiga/p9vmj/.

@gurdiga мне кажется, что у вашего обещания есть своя ошибка отлова. Попробуйте добавить к своему обещанию .catch(done) и я думаю, это сработает так, как задумано.

@hallas Вау: это был ответ! :) aPromise.finally() кажется, идеально подходит для помещения done : он также должен вызываться при выполнении обещания. ;)

Спасибо!

Я чувствую себя глупо.

Думаю, я наконец понял: когда что-то бросается в любую из функций обработчика обещания, будь то та, что передана в .then() , в .catch() или в .finally() , ошибка обрабатывается библиотекой обещаний. Таким образом, средство выполнения тестов никогда не видит реальной ошибки, done() никогда не вызывается (потому что что-то до того, как оно выдало ошибку утверждения), и поэтому ошибка тайм-аута - это все, что вы получаете от средства выполнения теста.

Чтобы выйти из обещания, я использую setTimeout() :

    it('returns the correct value', function(done) {
      var returnValue = 5;

      aPromise.then(function() {
        setTimeout(function() {
          expect(returnValue).to.equal(42);
          done();
        });
      });
    });

Я обнаружил, что это единственный способ получить правильные сообщения об ошибках и поведение бегуна тестов.

Если done передано в .catch() или .finally() тест в любом случае считается пройденным, поэтому, если есть ошибки утверждения, вы их никогда не увидите.

Я столкнулся с аналогичной проблемой и в конце концов понял, что вы не должны использовать done при тестировании асинхронных функций с обещаниями, вместо этого просто верните обещание. Таким образом, вы сможете сделать это без тайм-аута, например:

it('returns the correct value', function() {
    var returnValue = 5;

    return aPromise.then(function() {
        expect(returnValue).to.equal(42);
    });
});

Я думаю, что этот вопрос все еще существует. У меня проблема с тайм-аутом, которую нельзя решить, вернув обещание, потому что мой модуль не использует обещания.

it("works", function(done) {
    new Something()
    .on("eventA", function(result) {
        expect(result).to.be.true;
    })
    .on("eventB", function(result) {
        expect(result).to.be.false;
        done();
    });
});
  • Оборачивание экземпляра в Promise кажется излишним.
  • Заключение каждого утверждения в try / catch также кажется излишним и, что более важно, приводит к Error: done() called multiple times .

Идеи:
http://staxmanade.com/2015/11/testing-asyncronous-code-with-mochajs-and-es7-async-await/

Что касается рекомендаций в блоге, я не знаю об обработке ошибок асинхронной функции, но для простых обещаний рекомендация try-catch выглядит странно: исходная попытка обещания без try-catch была почти правильной, нужно было только использовать .catch(done) вместо использования второго параметра в then как done . (Конечно, поскольку Mocha имеет прямую поддержку обещаний, вы также можете просто вернуть обещание, но чего бы оно ни стоило ...) Проблема в исходном примере обещания заключалась не в отсутствии try-catch, а в том, что второй обработчик to then не вызывается с исключениями, создаваемыми первым обработчиком, тогда как следующий catch ; Я не знаю, в чем заключалась причина того, что обещания были построены таким образом, но обещания таковы. Кроме того, если по какой-то причине было несколько then потребуется только один финальный .catch(done) , что является преимуществом над try-catch внутри обработчиков (над тот факт, что .catch(done) изначально менее шаблонный).

Что касается вашего API:

  1. Вы уверены, что оба события вызываются и в правильном порядке? Если бы это было не так, как бы ваш тест превратил это в нормальный сбой?
  2. Что обычно происходит с исключениями, которые генерируются обработчиками событий? Если они не распространяются так, как в синхронном коде, а вместо этого предназначены для прослушивания в API (например, с помощью .on("error", function(error) {...}) ), они никогда не достигнут Mocha, если вы не прислушаетесь к ним и не получите слушатель вызывает done с ошибкой (или просто используйте done со слушателем, если слушателю передается ошибка в качестве первого параметра, например, .on("error", done) . Предположительно, это также только нужно писать один раз для каждого теста, а не один раз для каждого обработчика событий, например .catch(done) в обещании.
  1. Да, и я использую событие "end" / "drain" чтобы проверить, были ли установлены логические значения в других событиях.
  2. Тайм-аут случается. Я пытаюсь найти более простую и чистую альтернативу.

Извините, я до сих пор не знаю, как ваш API должен сообщать об ошибках.

Да, до сих пор я просто полагался на тайм-ауты, когда что-то выходит из строя, а затем вручную копался, чтобы узнать, как / почему. К счастью, вещи редко ломаются, но это не оправдание отсутствия лучшего дизайна (в основном с моей стороны).

@stevenvachon : Простите меня заранее, но я не вижу немедленных проблем с вашим примером. Утверждения, сделанные в ваших слушателях событий, должны обрабатываться Mocha через отображение uncaughtException (если только реализация эмиттера событий не улавливает ошибки слушателя и не генерирует событие error или что-то в этом роде, что тогда по-прежнему легко решать).

Теперь, если ваша внутренняя реализация использует обещания, но генерирует события, а не раскрывает обещание, ваши утверждения действительно будут «съедены». Я могу обойти эту проблему, используя unhandledRejection .

Обычно я помещаю это в сценарий установки, который запускается перед тестами:

process.on('unhandledRejection', function (reason)
{
    throw reason;
});

Примечание. Для работы в браузерах может потребоваться дополнительная смазка.

Я надеюсь увидеть, что Mocha поддерживает это так же, как uncaughtException поскольку это обычный вариант использования; то, что я использую обещания, не означает, что я хочу вернуть их вызывающей стороне!

Такая же проблема с

    it('Convert files into base64', (resolve) => {
        let files = Promise.all(promises);

        return files
            .then(([actual, expected]) => {
                assert.equal(actual, expected, 'Files not are equal');
                resolve();
            })
            .catch(error => resolve);
    });
   Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.

.catch неверно. error => resolve эквивалентно function(error) { return resolve } , что означает, что resolve не будет вызываться и ошибка игнорируется. Вы хотите вызвать resolve с ошибкой, которая будет error => resolve(error) . Конечно, передача функции обратного вызова X которая просто вызывает функцию Y с теми же аргументами, что и X эквивалентна простой передаче Y в качестве обратный вызов, поэтому даже .catch(error => resolve(error)) можно упростить до .catch(resolve) . (Вам нужно только не передавать resolve напрямую, если вы передаете его в then и, следовательно, необходимо избегать передачи параметра результата then в resolve чтобы предотвратить его обработку как ошибку: then(()=>resolve()) а не просто .then(resolve) ; но поскольку вы используете обратный вызов then для утверждений, это не возникает .)

(Кроме того, идиоматически, resolve здесь, вероятно, следует назвать чем-то вроде done , поскольку он обрабатывает как успех, так и неудачу, и судит, что зависит от того, был ли он вызван с аргументом или нет. Отсюда и название в сообщении об ошибке Mocha. Но это может быть спорным вопросом; читайте дальше.)

Однако в этом случае вы можете упростить его еще больше, просто вернув обещание и вообще не используя параметр test done, так как Mocha будет ждать, пока обещание не будет выполнено или завершится неудачно, как указание на успешность или неудачу теста (при условии, что нет параметра done для тестовая функция; поведение в случае, если оба используются, все еще хешируется):

it('Convert files into base64', () => {
    let files = Promise.all(promises);
    return files
        .then(([actual, expected]) => {
            assert.equal(actual, expected, 'Files not are equal');
        })
});

@lsphillips, который мне

Мне потребовалось время, чтобы разобраться с этим! Основываясь на ответах выше , это два варианта:

npm install --save mocha expect.js q
./node_modules/mocha/bin/mocha test.spec.js

// test.spec.js

var $q = require('q');
var expect = require('expect.js');

describe('tests with done', function(){
    it('returns the correct value from promise', function(done) {
      var returnValue = 5;
      var def = $q.defer();
      def.promise.then((val) => {
        expect(val).to.equal(42);
        done();
      }).catch(done);
      def.resolve(returnValue)
    });
})

describe('tests returning promises', function(){
    it('returns the correct value from promise', function() {
      var returnValue = 5;
      var def = $q.defer();
      def.resolve(returnValue)
      return def.promise.then((val) => {
        expect(val).to.equal(42);
      });
    });
})
  tests with done
    1) returns the correct value from promise

  tests returning promises
    2) returns the correct value from promise


  0 passing (15ms)
  2 failing

  1) tests with done returns the correct value from promise:
     Error: expected 5 to equal 42
      at Assertion.assert (node_modules/expect.js/index.js:96:13)
      at Assertion.be.Assertion.equal (node_modules/expect.js/index.js:216:10)
      at def.promise.then (tests/test.spec.js:9:24)
      at _fulfilled (node_modules/q/q.js:854:54)
      at self.promiseDispatch.done (node_modules/q/q.js:883:30)
      at Promise.promise.promiseDispatch (node_modules/q/q.js:816:13)
      at node_modules/q/q.js:570:49
      at runSingle (node_modules/q/q.js:137:13)
      at flush (node_modules/q/q.js:125:13)
      at _combinedTickCallback (internal/process/next_tick.js:67:7)
      at process._tickCallback (internal/process/next_tick.js:98:9)

  2) tests returning promises returns the correct value from promise:
     Error: expected 5 to equal 42
      at Assertion.assert (node_modules/expect.js/index.js:96:13)
      at Assertion.be.Assertion.equal (node_modules/expect.js/index.js:216:10)
      at def.promise.then (tests/test.spec.js:22:24)
      at _fulfilled (node_modules/q/q.js:854:54)
      at self.promiseDispatch.done (node_modules/q/q.js:883:30)
      at Promise.promise.promiseDispatch (node_modules/q/q.js:816:13)
      at node_modules/q/q.js:570:49
      at runSingle (node_modules/q/q.js:137:13)
      at flush (node_modules/q/q.js:125:13)
      at _combinedTickCallback (internal/process/next_tick.js:67:7)
      at process._tickCallback (internal/process/next_tick.js:98:9)

@gurdiga Спасибо за идею setTimeout ()! У меня была аналогичная проблема, но теперь я могу, по крайней мере, получать правильные сообщения об ошибках!

В моем сценарии я использовал Nightmare для завершения тестов. Решение для меня было использовать .catch(done) . Вы можете вызвать done(error) внутри другого обратного вызова catch, как в примере ниже.

describe('Clicking in any bad reputation tag', () => {
    it('open the bad reputation modal', (done) => {
      nightmare
        .select('#per-page', '50')
        .waitForAjax()
        .click('[data-reputation="bad"]')
        .evaluate(function() {
          return document.querySelector('.vue-modal .ls-modal-title').innerText
        })
        .then(function(title) {
          title.should.equal('Sua segmentação teve uma avaliação ruim!')
          done()
        })
        .catch((error) => {
          screenshot(nightmare)
          done(error)
        })
    })
  })

@itumoraes, который работает, но вы можете сделать это вместо этого:

describe('Clicking in any bad reputation tag', () => {
    it('open the bad reputation modal', () => {
      return nightmare
        .select('#per-page', '50')
        .waitForAjax()
        .click('[data-reputation="bad"]')
        .evaluate(function() {
          return document.querySelector('.vue-modal .ls-modal-title').innerText
        })
        .then(function(title) {
          title.should.equal('Sua segmentação teve uma avaliação ruim!')
        })
    })
  })

Вам не нужно вызывать done() если вы возвращаете обещание. См. Мое сообщение в блоге Использование Async / Await с Mocha, Express и Mongoose

Я использовал приведенный ниже сценарий, но получил ту же ошибку превышения тайм-аута.

Myscript:

описать ("getBillingDetail", async function () {
this.timeout (55000);
it.only ("проверить правильность имени задания", асинхронная функция (выполнено) {
this.timeout (55000);
var result = await url.getBillingDetail ('12254785565647858');
console.log (результат);
assert.equal (результат, истина);
});
});

Ошибка: превышено время ожидания 55000 мс. Для асинхронных тестов и ловушек убедитесь, что вызывается "done ()"; при возврате обещания убедитесь, что оно разрешено.

Перестаньте писать одно и то же в нескольких закрытых выпусках. Не передавайте выполненный обратный вызов асинхронным функциям. Прочтите документацию по асинхронным тестам

@Munter я удалил обратный вызов done, но эта ошибка возникает снова

Похоже, ваше обещание так и не было выполнено.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги