Mocha: Ошибка: метод разрешения слишком задан.

Созданный на 2 авг. 2016  ·  84Комментарии  ·  Источник: mochajs/mocha

Этот:

before(done => {
    return Promise
        .all([])
        .then(() => Model.insert(...)) // Bookshelf model returning a Bluebird Promise
        .then(() => done())
        .catch(done)
})

приведет к ошибке Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.

Документы говорят:

В Mocha v3.0.0 и новее возврат Promise и вызов done () приведет к исключению, так как обычно это ошибка:

Вызов модели разрешается с помощью Promise.<Object> вновь вставленной записи, однако, если я опущу .then(() => done()) тогда таймауты теста.

confirmed-bug

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

async functions (babel) с done тоже ломаются.

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

async functions (babel) с done тоже ломаются.

Как указано в сообщении об ошибке, вы не должны предоставлять функцию с арностью> 0 (что означает, что она принимает обратный вызов done ), _and_ возвращает Promise.

Самый простой способ исправить это - опустить return , но, поскольку вы используете обещания, я предлагаю вместо этого избавиться от обратного вызова done , так как это приведет к гораздо более простой конструкции :

before(() => Promise.all([]).then(() => Model.insert(...)));

Вот пример как async (по сути, обещание), так и done которое нарушается:

it('fires change event when calling blah.doSomethingThatFiresChange', async function (done) {
  const blah = await getBlah()
  blah.on('change', () => done())
  blah.doSomethingThatFiresChange()
})

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

it('fires change event when calling blah.doSomethingThatFiresChange', async function () {
  const blah = await getBlah()
  return new Promise(resolve => {
    blah.on('change', resolve)
    blah.doSomethingThatFiresChange()
  })
})

... но я предполагаю, что вы имеете в виду, что в этом примере было бы лучше, если бы мокко ждал, пока оба обещания будут выполнены, и будет вызван обратный вызов?

К сожалению, именно эта комбинация чаще всего является ошибкой в ​​тесте, поэтому это сообщение об ошибке было добавлено в первую очередь.

... если я опущу .then (() => done ()), тогда таймауты теста.

В любом случае это похоже на ошибку.

@ScottFreeCode Хм, да, похоже, это из-за того, что ошибка "overspecified" выдается в функции, представленной как обратный вызов done : https://github.com/mochajs/mocha/blob/4944e31ff60105815f4b314996a9861e73f6bfd2/lib runnable.js # L357 -L373

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

@ScottFreeCode Что здесь за исправление?

Я собираюсь предположить, что это «подождать, пока обещание разрешится или отклонится, а затем отклонить с ошибкой« чрезмерно задано »?

Если мы собираемся рассматривать обещание done plus в том же тесте как ошибку, я не вижу причин не обнаруживать ошибку, как только тестовая функция, взявшая done возвращает обещаю, как предложил @papandreou . Для меня не имеет большого смысла пытаться выяснить, какие еще моменты должны вызывать ошибку, если только мы не собираемся разрешать promises и done вместе в некоторых случаях.

@ScottFreeCode Я согласен. Так что это

  1. Обнаружить проблему; создать экземпляр Error но не вызывать done() с ним
  2. Дождитесь выполнения обещания
  3. Отклонить с помощью Error

Бонусный вопрос: Что делать с результатом выполнения Обещания?

Ах, я думаю, я понял - даже когда мы обнаруживаем ошибку, нам нужно позволить тесту выполняться, чтобы он еще не выполнял свой код во время следующего теста, и, возможно, чтобы мы также могли сообщить результат теста.

Может ли тест закончиться вызовом done без разрешения или отклонения обещания? Если так, то это еще один крайний случай, с которым нам придется справиться.

Моя склонность, re. что делать с результатом теста, так это то, что если он истечет (без _either_ вызвал done или разрешил / отклонил обещание), мы должны просто сообщить об использовании done с обещанием (потому что путаница по поводу комбинации вполне может быть причиной того, что она не дошла ни до одной, а вместо этого истекло время ожидания), но если ей все же удается закончить каким-либо образом, то предположительно результат действителен (или, по крайней мере, имеет какое-то значение), и мы должны сообщить как возможное ошибочное использование done вместе с обещанием, так и результат теста в надежде, что это хоть немного поможет.

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

Что ж, мы можем делать это поэтапно. Первый заключается в том, чтобы гарантировать, что если будет возвращено Promise и дан обратный вызов done , то Mocha сломается дружественным образом.

_Постижимо_, что можно было сделать что-то вроде этого (с Bluebird):

// to make this work, you need to omit `return`
it('should do something async for sure', function(done) {
  return somethingAsync()
    .then(makeAssertion)
    .asCallback(done);
});

Но это просто то, что вы могли бы сделать; Мне еще предстоит определить вариант использования для этого.

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

Это скорее проблема типа «плохой интерфейс».

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

it("should not time out", function(done) {
  return new Promise(function(resolve, reject) {
    setTimeout(resolve.bind(42), 50)
  })
})

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

Я смотрю на пиар в любом случае ...

Заинтересованным сторонам следует смотреть №2413.

@briansipple

Я получаю ту же ошибку, используя несколько хуков async beforeEach в версии 3:

  beforeEach((cb) => connection.db.collection('users').remove({}, cb));
  beforeEach((cb) => connection.db.collection('accounts').remove({}, cb));
  beforeEach((cb) => connection.db.collection('accounts').insertOne(account1, cb));

Невозможно по-прежнему использовать async-хуки таким образом?

Я понял свою проблему: мои методы внутри каждого крючка возвращают promise (я не знал этого, потому что в этих случаях я не использовал обещания), и, очевидно, я также передаю функцию cb .

Не думаю, что это хорошая идея. Если кто-то запрашивает обратный звонок, я думаю, что они ясно изложили свои намерения. Ошибка здесь просто досада. Пожалуйста, удалите это.

На самом деле это не вопрос намерений. Из описания # 1320:

Когда указана и функция обратного вызова, и объект Promise.
возвращается, условие разрешения Runnable неоднозначно.

Это не двусмысленно, обратный звонок был запрошен. Как это неоднозначно?

Я согласен с тем, что @RobertWHurst здесь нет двусмысленности.

Кроме того, я думаю, что этот вопрос может быть чем-то «основанным на мнении», и разработчики будут иметь разные точки зрения. Я считаю, что радикальные изменения и принуждение людей к этому - это очень экстремистский подход.

Мы никого не заставляли ничего делать. Мы выпустили мажор, в котором по определению будут критические изменения. Семверь мы не ломали.

Я не думаю, что он имел в виду, что ты сломал семвер, я думаю, он имел в виду, что ты сломал Мокко. Это изменение не облегчает жизнь разработчикам, а усиливает мнение.

beforeEach((cb) => connection.db.collection('users').remove({}, cb));
            ^^

^^ Это не двусмысленно. Совершенно ясно, чего ожидает автор. Автор сделал все возможное, чтобы запросить обратный звонок.

На самом деле причина, по которой вы получаете исключение, заключается в максимальном избегании принудительного заключения. Mocha не имеет мнения о том, являются ли возвращенное обещание или обратный вызов авторитетным. Вы не можете иметь и то и другое одновременно, поскольку это приводит к неоднозначным результатам. Неоднозначные результаты в тестовой среде следует рассматривать как ошибку. Следовательно, сообщение об ошибке, которое поможет вам узнать об этом и сделать выбор и изменить в соответствии с вашим мнением.

Было бы полезно уменьшить драматизм. "Ты сломал Мокко" никому не помогает. Повышение основной версии semver явно определяется как критические изменения, которые могут потребовать корректировки кода. Вы можете остаться на версии 2.x, чтобы дать вам время внести изменения для исправления ваших тестов. Это эволюция, а не поломка

@Munter Я все еще не понимаю, насколько неоднозначен запрос обратного вызова. Если вы попросите обратный звонок, вы должны его использовать. В противном случае это ошибка программирования. Это явное действие со стороны автора теста.

Если вы чувствуете драму, я имею в виду ее отсутствие. «Ты сломал Мокко» не должно быть гиперболическим. Я просто думаю, что это противоречит дизайну модуля и нарушает исходный контракт api.

Как было сказано ранее, babel async / await не работает с новым mocha @ 3. Пример:

it('should fill userName and password', async function (done) {
    const userNameField = global.driver.wait(until.elementLocated({css: '#username'}));
    userNameField.sendKeys('admin');

    const passNameField = global.driver.findElement({css: '#password'});
    passNameField.sendKeys('*******');

    const userNameVal = await userNameField.getAttribute('value');
    const passwordVal = await passNameField.getAttribute('value');

    try {
      assert.equal(userNameVal, 'admin');
      assert.equal(passwordVal, 'changeme');
    } catch (error) {
      done(error);
      return;
    }

    done();
  });

Этот код хорошо работает с mocha @ 2, но не с mocha @ 3, потому что в await он возвращает обещание.

Думаю этот пиар здесь актуален => https://github.com/mochajs/mocha/pull/2413
Более сложная работа с крайними случаями этой ошибки.

@artyomtrityak Это отличный пример того, что done не требуется.

Недоброжелатели высказали свое мнение. Тем не менее, разработчики Mocha не согласны с аргументами в пользу отмены этого изменения. Эран Хаммер сказал (перефразировав): «Как сопровождающий, одна из самых сложных вещей, которые вы можете сделать, - это разочаровать тех, кто хочет продвинуть работу в своем направлении».

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

Если вам не нравится это изменение, и вы просто не можете конструктивно относиться к нему, это OSS - вы можете разветвить проект и отменить изменения там.

@boneskull преобразуется в асинхронные функции, которые возвращают обещания, мне не нужно завершать мой тестовый пример, когда обещание будет выполнено, но нужно выполнить некоторые пользовательские проверки результатов. Как я уже сказал, этот код отлично работает с mocha @ 2, но с mocha @ 3 - нет. Моя команда (~ 20 чел.) Не может перейти на последнюю версию мокко из-за этого.

В настоящее время mocha 2.x обеспечивает большую гибкость, есть ли какие-либо технические причины для этого изменения?

@artyomtrityak Это отличный пример того, что делать не нужно.

Не могли бы вы привести пример, как это должно выглядеть с синтаксисом babel async/await и без return new Promise ?

@artyomtrityak Как насчет того, чтобы полностью принять обещания? Тогда вы можете сократить свой тест до:

it('should fill userName and password', async function () {
    const userNameField = global.driver.wait(until.elementLocated({css: '#username'}));
    userNameField.sendKeys('admin');

    const passNameField = global.driver.findElement({css: '#password'});
    passNameField.sendKeys('*******');

    const userNameVal = await userNameField.getAttribute('value');
    const passwordVal = await passNameField.getAttribute('value');

    assert.equal(userNameVal, 'admin');
    assert.equal(passwordVal, 'changeme');
  });

Если вы чувствуете себя атакованным, то я думаю, вам нужно пересмотреть, насколько эмоционально вы вовлечены в этот разговор. Ни один из комментариев не был личным. Я уверен, что вы все молодцы, особенно если учесть, что вы пожертвовали свое время на поддержку этого проекта, который мы очень ценим и заслуживаем.

Большинство из вас (текущие активные сопровождающие) начали работать над Mocha примерно в середине 2014 года. Mocha уже был создан к тому моменту, когда вы, ребята, начали вносить свой вклад. Это просто мое мнение, но я не думаю, что я одинок, кто считает, что нельзя вносить критические изменения в установленную библиотеку, если это не оправдано. Хотя я могу представить себе первоначальное оправдание этого изменения, оно не выдерживает критики, если указать на следующее. Запрос обратного вызова сообщает о четком намерении. Обещания не так ясны, потому что они не запрашиваются, они возвращаются, что может произойти косвенно и случайно (например, из сторонней библиотеки). Из-за этих различий два способа получения не равны, и поэтому попытка использовать оба не совсем двусмысленна. Обратные вызовы должны быть записаны в аргументы теста. Вы не можете сделать это с помощью обещаний, поэтому, запрашивая обратный вызов, вы явно сообщаете о своих намерениях. Ваше сообщество выразило эту озабоченность, и вместо того, чтобы признать ошибку, вы, ребята, удваиваетесь. Кажется, вы даже подумываете о том, чтобы тесты были асинхронными, чтобы гарантировать, что эта ошибка действует последовательно. См. => Https://github.com/mochajs/mocha/pull/2413. Похоже, большое изменение для сообщения об ошибке защищает от маловероятной ошибки.

Ребята, вы проделали огромную работу по поддержанию этой библиотеки с момента ухода

Полностью согласен с @RobertWHurst.

Запрос done должен переопределить поведение возвращенного обещания. Маловероятно, что done будет запрашиваться, когда в этом нет необходимости, и сценарии сгенерированных событий в функции async являются прекрасным примером.

Из моего комментария выше:

it('fires change event when calling blah.doSomethingThatFiresChange', async function (done) {
  const blah = await getBlah()
  blah.on('change', () => done())
  blah.doSomethingThatFiresChange()
})

По мере того, как все больше людей переходят на ES6 / 7 + async / await , это станет обычным делом при использовании Mocha.

Пожалуйста, пересмотрите это изменение.

@RobertWHurst Вы утверждаете, что определение обратного вызова done является явным намерением. Не является ли выражение return явным намерением? Оба они определены вами в вашем коде. Как мы можем решить, что одна часть вашего кода является преднамеренной, а другая - нет? Если вы представите мир до () => foo любой оператор возврата всегда был бы явным. Единственная причина, по которой вы сейчас взволнованы, заключается в том, что вы начали использовать неявные операторы возврата, что, как я могу думать, является только эстетическими причинами.

Учитывая, что Mocha часто используют новички, которые обычно копируют / вставляют примеры, которые, скорее всего, содержат обратный вызов done , как бы вы справились со случаем, когда этот новый пользователь явно возвращает обещание, но получает тайм-аут? Это результат, если изменение, от которого вы злитесь, будет отменено.

Текущее поведение гораздо более ясно показывает, что не так, чем просто неожиданный тайм-аут.

@Munter С async на картинке я думаю, что возвращенное обещание имеет более низкие оценки по шкале явности, потому что оно создается и возвращается автоматически, независимо от того, используете ли вы await :

it('should work with a async function that could have been sync', async function () {
  assert.ok(true);
});

it('should work with a legitimately async function', async function () {
  assert.equal(await getBlah(), 'blah');
});

it('should work with a fully spelled-out Promise-based test', function () {
  return getBlah().then(function (blah) {
    assert.equal(blah, 'blah');
  });
});

И еще есть спорный вопрос:

it('should foo', async function (done) {
  assert.equal('foo', 'foo');
  done();
});

Этот крохотный async также легко может случайно проникнуть внутрь, поэтому мы должны думать (по крайней мере) о первом примере как о новом виде ошибки, как указывает @elado .

После небольшого разговора здесь => https://github.com/mochajs/mocha/pull/1320 у меня появилась идея альтернативного решения проблемы. Я добавил PR для вашего удовольствия от обзора здесь => https://github.com/mochajs/mocha/pull/2454

: пиво:

Вы утверждаете, что определение обратного вызова done является явным намерением. Не является ли выражение возврата явным намерением?

@Munter Не забывайте, что выражения стрелочных функций coffeescript и es6 возвращают _неявно_, поэтому вы можете сделать что-то вроде

it 'should foo', (done) -> request('/foo').end (err, res, body) -> done()

и думаю, что ты в безопасности. Но это означает, что вам нужно преобразовать этот красивый однострочный текст во что-то вроде

it 'should foo', (done) ->
  request('/foo').end (err, res, body) -> done()
  return

Это как раз наша проблема с нашей общей кодовой базой в каждом и mecano . Учти это:

it 'should foo', (done) ->
  obj =
    run: ->
      done()
      @
    then: -> "what the hell, i'm not a promise"
  obj.run()

Это не совсем специфично для coffeescript, но возвращение неявных значений делает его хуже. Mocha должен обнаруживать допустимые экземпляры обещаний. Кроме того, возможно, параметр может отключить эту функцию.

Всем привет,
Разберитесь в этой проблеме с помощью следующего кода;

describe('Page', ->
  describe('/GET home', ->
    it('it SHOULD return the homepage', (done) ->
      chai.request(app).get('/').end((err, res) ->
        res.should.have.status(200)
        res.text.should.be.a('string')
        done()
      )
    )
  )
)

Эта проблема решена путем соблюдения цепочки обещаний и исключения обратного вызова done() .

describe('Page', ->
  describe('/GET home', ->
    it('it SHOULD return the homepage', ->
      chai.request(app).get('/').then((res) ->
        res.should.have.status(200)
        res.text.should.be.a('string')
      )
    )
  )
)

Надеюсь, это поможет другим, столкнувшимся с подобной ошибкой: smile:

PS : сердце: Mocha btw: +1:

ИЗМЕНИТЬ Удалить catch() на основе комментария

@karlbateman Спасибо. Однако вам не нужен последний .catch . Отклоненное обещание засчитывается Mocha как ошибка

@Munter Понятно , спасибо: smiley:

Подумав еще немного, мне действительно нравится поведение ожидания как обещания, так и обратного вызова. Был ли достигнут какой-либо прогресс во внедрении этого?

@ light24bulbs Спасибо за отзыв! Нет, черт возьми, над этим никто не работал, потому что это просто идея, которую я добавил, чтобы увидеть реакцию. Я только что проверял, были ли какие-либо дополнительные отзывы об этой идее, какие-либо случаи, в которых она не работает, и т. Д.

Есть ли обходной путь при использовании babel?

Есть ли обходной путь при использовании babel?

Оборачиваю дважды:

it("should work", done => {
  (async () => {
    await something;
    done();
  })();
});

@SaschaNaz спасибо, это работает в v3.2.0 :)

Спустя 6 месяцев эта проблема все еще тормозит все современные js-тесты ...
стыд

@benfavre Спасибо за обнадеживающие слова, которые определенно мотивируют добровольцев взять на себя ответственность за реализацию любого решения, которое вы не указали в свободное время, вместо того, чтобы играть со своими детьми

@Munter Не волнуйтесь, рад, что смог помочь определить конкретную проблему, с которой я столкнулся как новый пользователь мокая.
@SaschaNaz предложил решение обернуть дважды, не помогло.

=> Использование исключительно обещаний сработало так, как должно.

В следующий раз, думаю, мне следует просто поставить +1, как моррон, чтобы меня не оскорбляли бесплатно.
В моем сообщении не было ничего оскорбительного, более того, мое утверждение остается верным.

Большинство людей просто выберут другой фреймворк ... на данный момент он просто сломан с помощью async / await, без четких указаний на главном сайте и четких сообщений об ошибке в cli.

С Новым годом и весело провести время, играя с детьми.

Остынь ...

Обсуждения превратились в «уделение внимания» как обратному вызову done и возвращенному Promise : https://github.com/mochajs/mocha/issues/2509

Закрытие. см. # 2509

На случай, если какое-то тело все еще борется ...

Есть ли обходной путь при использовании babel?

Теперь со встроенным обработчиком обещаний, упомянутым в # 2509, нам не нужно использовать wrapping twice hack следующим образом:

it("should work", done => {
  (async () => {
    await something;
    done();
  })();
});

Вместо этого мы можем просто использовать это:

it("should work", async () => {
  await something;
});

Проверьте этот пост для более подробной информации

@ lo-tp
1.) этот код работает, когда он скомпилирован с помощью babel?

it("should work", async () => {
  await something;
});

@SerkanSipahi

  • Да, чтобы использовать синтаксис async/await сейчас, нам нужна помощь babel.

Не уверен, что я что-то упустил ... но я решил эту проблему, не используя никаких операторов return с моими обещаниями и просто полагаясь на done ().

Вот пример, работающий для меня

  describe('STRIPE CHARGES: Get Charges', () => {
    it('should list customer charges', async () => {
      const charges = await Stripe.getChargesList(customerObj);
      charges.data[0].should.have.property('id');
      charges.data[0].amount.should.have.property('id');
    });
  });

Я использую мокко 4 с chai.assert.

Сначала я попытался использовать обратный вызов done () вот так.

it('should throw an error', async function(done) {
  try {
    result = await something('value that causes something() to throw an Error');
    done('failed');
  } catch (e) {
    done();
  }
});

Это не удалось с

Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both."

Эта ошибка и привела меня сюда, на эту страницу. Пройдя через этот довольно длинный поток (и я признаю, что не полностью понял все споры), могу ли я понять, что мне вообще не следует использовать done () с асинхронными вызовами, потому что они основаны на обещаниях?

Если да, то как мне выполнить следующий тест, когда вызов await something () выдает ошибку, и это то, чего я ожидал?

it('should throw an error', async function() {
  try {
    result = await something('value that causes something() to throw an Error');
  } catch (e) {
    // how to indicate that this should happen, if I can't call done() to tell mocha?
  }
});

Может кто-нибудь помочь мне разобраться в этом конкретном случае, пожалуйста? Следует ли мне использовать библиотеку утверждений или мне нужно добавить что-то еще в приведенный выше код?

Большое спасибо.

Низкоуровневое и подверженное ошибкам решение:

it('should throw an error', async function() {
  try {
    result = await something('value that causes something() to throw an Error');
    throw new Error('Promise unexpectedly fulfilled');
  } catch (e) {
    // Optionally assert something about e...
  }
});

Я бы использовал библиотеку утверждений в любой день недели:

const expect = require('unexpected');
it('should throw an error', async function () {
    await expect(something(), 'to be rejected with', 'the error message');
});

Или чай + чай как обещано:

const chai = require('chai');
chai.use(require('chai-as-promised'));
const expect = chai.expect;

it('should throw an error', async function () {
    await expect(something()).to.be.rejectedWith('argh');
});

Привет, ребята, этот хорошо работает с asyncawait (просто опустите 'done ()')

describe('New order from landing', function() {
    describe('check new client function', function () {
        it("must check db and return that random client is new", async function () {
            var result = await handler.checkClient(reqNewClient.body)
                expect(result).to.have.property('newclient').equal(true)
            })
    })
})

результат:

Новый заказ с лендинга
проверить новую клиентскую функцию
√ должен проверить db и вернуть, что случайный клиент новый
1 проход (9 мс)

@kolykhalov Спасибо У меня сработало

В моем случае я заключил блок async в команду try / catch

it('It should activate a user.', async() => {
        return new Promise((resolve, reject) => {
            try {
                // Get the last activation token from the DB
                let sql = 'select * from activation_tokens order by created_at desc limit 1';
                let res = await DB.query(sql);
                let {token} = res[0];

                server
                    .post('/auth/activate')
                    .send({token})
                    .set('Content-Type', 'application/x-www-form-urlencoded')
                    .expect('Content-Type', /json/)
                    .expect(200)
                    .end((err, res) => {
                        res.body.data.should.contain.keys('message');
                        res.body.data.message.should.equal("Activated");
                        resolve();
                    });
            } catch (e) {
                console.error(e);
            }
        });
    });

Обновлен, чтобы избежать уязвимостей. Теперь мне нужно переписать 99 тестов. FML

Итак, чтобы прояснить, в чем проблема, поскольку некоторые говорят, что вам не нужно использовать done и async , вот пример, в котором вы хотели бы использовать оба.

it('should error on fn', async function(done) {
  try {
    await fn();
  } catch (e) {
    // assert some things here
    done()
  }
});

Если не использовать done , тест будет пройден, если не будет выдано никаких ошибок. Некоторые предлагают использовать такие вещи, как expect(await fn).to.throw('blah') , но иногда вам нужно проверить больше свойств, чем уместится в однострочном.

Я получал эту ошибку, но случайно возвращал обещание (супертест)

it('Should do something', (done) => {
       return supertest(server)
           .get(`/api/endpoint`)
           .set('somekey', 'somevalue')
           .expect(200)
           .then(() => { done(); })
           .catch(done);
});

Удаление предложения о возврате решило проблему

Для всех, кого это сводило с ума ...

Это работает:

it("should work", async () => {
  await something;
});

Это не так:

it("should work", async (done) => {
  await something;
});

Вы должны удалить done как параметр.

@tamoyal , да - как писал Эладо 3 августа 2016 г.

Использование async / await возвращает неявный Promise . Обратный вызов done используется для
Асинхронный режим на основе CPS , как показано здесь . Mocha поддерживает и то, и другое ... но не одновременно.
Это задокументировано и является стандартным поведением с момента выпуска Mocha-3.0.

Я получал эту ошибку, но случайно возвращал обещание (супертест)

it('should do something', (done) => {
       return supertest(server)
           .get(`/api/endpoint`)
           .set('somekey', 'somevalue')
           .expect(200)
           .then(() => { done(); })
           .catch(done);
});

Удаление предложения о возврате решило проблему

@victorsferreira , похоже, это должно было быть решением ...

it('should do something', () => {
  return supertest(server)
    .get(`/api/endpoint`)
    .set('somekey', 'somevalue')
    .expect(200);
});

@tamoyal Ага, вот что меня

@mienaikoe , этот точный сценарий явно задокументирован , понимаете ...

@plroebuck две вещи:

1) Есть разница между документированием чего-либо и одной из немногих библиотек в npm, которая анализирует параметры функции. Я мог бы задокументировать, что проверяю биографию всех своих друзей, когда впервые встречаюсь с ними, но это все равно странно, и люди все равно будут жаловаться на это, если у меня не будет причины и я не объясню им причину.

2) В документации есть недоработка:

В Mocha v3.0.0 и новее возврат Promise и вызов done () ...

Речь идет не о вызове done, а о _определении done в качестве параметра_ независимо от того, используете вы его или нет.

@mienaikoe

  1. Mocha проверяет второй параметр it чтобы увидеть, существует ли функция, и (если да) ее арность, чтобы определить, добавил ли пользователь обратный вызов. Это едва ли можно назвать самоанализом и обычно используется в JavaScript.
  2. Отправьте PR, чтобы исправить документацию, если считаете, что она слишком расплывчата.

Удаление сделано как параметр сработало для меня!

ДО:

image

ПОСЛЕ (работает!)

image

@QauseenMZ , не вижу разницы между вашим кодом «до» и «после».

Вы также не вернете свое обещание. Разве это не должно быть больше похоже на следующее?

  it('should receive successful RPC response', async () => {
    return await getResponse(unitData)
      .then((res) => {
        expect(res).to.have.status(200);
      });
  });

PS. Ваш порядок аргументов обратного вызова Node нарушен ... error _always_ идет первым.

@plroebuck Спасибо за упоминание! Я только что отредактировал это.

Я не обрабатываю обещание, потому что функция getResponse возвращает обратный вызов. Это был единственный способ заставить его работать. Функция getResponse выглядит следующим образом:

image

Здесь ошибка является вторым параметром только потому, что функция обратного вызова возвращает getResponse. Пожалуйста, дайте мне знать, что вы думаете об этом. Благодаря!

Некоторые части кажутся нелогичными. Просто для ясности, какой пакет request вы используете?
Зачем вам нужно возвращать объект options (то, что вы назвали unitData )?

  • Откуда взяли obj ?
  • Зачем вам res.body.error с res.statusCode === 200 ?

PS. Пожалуйста, просто вставьте сам код, а не изображения кода ...

Для всех, кого это сводило с ума ...

Это работает:

it("should work", async () => {
  await something;
});

Это не так:

it("should work", async (done) => {
  await something;
});

Вы должны удалить done как параметр.

@tamoyal Ты спас мне жизнь <3

Это ломается и приводит к той же ошибке:

it('Location Querying Works', async () => {
    await WeatherComponent.pullLocationData();
    Vue.nextTick(()=>{
      expect(WeatherComponent.$el.textContent).contain("Spain")
    })
  })

Чтобы быстро решить эту проблему, оберните весь тест обещанием и используйте resolve как если бы вы использовали done .

Включите это:

it.only("Works with the database", async (done) => {
    let browser = await puppeteer.launch();
    let query = db.collection('lists').where('ownerId', '==', 'UJIXXwynaCj8oeuWfYa8');
    let ct = 0;
    query.onSnapshot(async querySnapshot => {
        if (ct === 0) {
            await addAPurpose(browser, session, sessionSig); // A function with a bunch of Puppeteer code.
            ct++
        } else {
            expect(querySnapshot.size).to.equal(1);
            expect(querySnapshot.docs[0].get().title).to.equal("Understand Mocha");
            done();
        }
        console.log(`Received query snapshot of size ${querySnapshot.size}`);
    }, err => {
        console.log(`Encountered error: ${err}`);
    });
});

в это:

it.only("Works with the database", async () => {
    const onSnap = new Promise(async (res, rej) => {
        let browser = await puppeteer.launch();
        let query = db.collection('lists').where('ownerId', '==', 'UJIo1gGMueoubgfWfYa8');
        let ct = 0;
        let unsubscribe = query.onSnapshot(async querySnapshot => {
            if (ct === 0) {
                await addAPurpose(browser, session, sessionSig);
                ct++
            } else {
                expect(querySnapshot.size).to.equal(1);
                expect(querySnapshot.docs[0].data().title).to.equal("Evolution");
                // done(); 
                unsubscribe();
                res();
            }
            console.log(`Received query snapshot of size ${querySnapshot.size}`);
        }, err => {
            console.log(`Encountered error: ${err}`);
            rej()
        });
    });
    return onSnap;
});

И работает так, как вы хотите.

Необходимость удалить done удивила меня, потому что тест выполнялся до тех пор, пока не было вызвано завершение. Чтобы сделать более очевидным, что done не следует использовать с async, асинхронные функции должны немедленно завершаться ошибкой, если done передается. Мокко следует начать тест с:

  1. Проверить, является ли функция асинхронной, и
  2. Обнаружение аргумента done .

Если они оба присутствуют, он должен бросить, вместо того, чтобы позволить моим тестам работать до тех пор, пока не будет вызван done а затем синий мяч. Затем ошибка должна предлагать вам заключить свой код в другое обещание и использовать resolve, как если бы вы использовали done .

Я знаю, что вы можете использовать function.prototype.name === "AsyncFunction" . Тогда это

if (function.prototype.name === "AsyncFunction && arg1.name === "done") {
  throw new Error("Can't use done with an async function")
}

чтобы реализовать это.

Я был почти СОЛНЕЧЕН по этому it в качестве функции синхронизации.

Решение:

it("It shouldn't be like this", function (done) {
    ( async function(){
        var life = require('InfiniteLife');
        var asyncRes = await life.someCoolstuff();
        assert(asyncRes);
        setTimeout( function(){
            done();
        },letsCallItQuitsNow);
    })();
});

Пример асинхронных функций с выполненным разрывом.

it('If the credentials exists in the system it should return the token generated against it.', async (done) => {
        let aObj = await admin.createAdmin();
        chai.request(server)
        .post("/authenticate")
        .set("Content-Type", "application/x-www-form-urlencoded")
        .send({username: aObj.login,password:aObj.password})
        .end((err, res) => {
            res.should.have.status(200);
            res.body.should.be.a("string");
            done();
        });
    });

Случай успеха

it('If the credentials exists in the system it should return the token generated against it.', async () => {
        let adminObj = await admin.createAdmin();
        chai.request(server)
        .post("/auth/login")
        .set("Content-Type", "application/x-www-form-urlencoded")
        .send({username: adminObj.login,password:adminObj.password})
        .end((err, res) => {
            res.should.have.status(200);
            res.body.should.be.a("string");
            // done();
        });
    });

@ Haisum92

В вашем тесте не требуется ни done ни async . Чай http возвращает обещание. Тесты Mocha работают с асинхронным кодом, если вы возвращаете обещание.

it('If the credentials exists in the system it should return the token generated against it.', () => {
  let adminObj = await admin.createAdmin();
  return chai.request(server)
    .post("/auth/login")
    .set("Content-Type", "application/x-www-form-urlencoded")
    .send({username: adminObj.login,password:adminObj.password})
    .end((err, res) => {
      res.should.have.status(200);
      res.body.should.be.a("string");
    });
});

@NateZimmer

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

it("It should be liek this", async function () {
  this.timeout(letsCallItQuitsNow);

  var life = require('InfiniteLife');
  var asyncRes = await life.someCoolstuff();
  assert(asyncRes);
});

Ваш пример также, похоже, не проваливает тест, если превышен тайм-аут, поэтому будьте осторожны в зависимости от его правильности

`Используя облачные функции express / js и firebase, проверьте ответы на ошибки API, затем получите токен и повторите попытку. Убедитесь, что если вы используете экспресс, вы возвращаете .json, а не .send, а также регистрирует ошибки с помощью mocha, поэтому используйте простой console.log.
Убедитесь, что вы используете async и полностью выполнили emit

  process.env.NODE_ENV='TESTING';
  const { FIREBASE_UID } = require('dotenv').config()?.parsed;
  const { red, green, white } = require('chalk');
  const chai = require('chai');
  const chaiHttp = require('chai-http');
  const server = require('../lib/api').API;
  const should = chai.should();
  const expect = chai.expect;
  chai.use(chaiHttp);
  const test = chai.request(server).keepOpen();

  const shouldPrint = (details, status) => {
      return white(`details: ${details}, status: ${status}`);
  };

describe('Authorization Middleware Error Check for bad token', () => {

    it(shouldPrint('Bad Request', red(400)), async () => {
        try {
            const res = await test.get('/').send()
            res.should.have.status(400);
            res.should.have.json
            const { details, status } = JSON.parse(res.text)
            expect(details).to.equal('Bad request')
            expect(status).to.equal(400)
        } catch (error) { 
            throw error 
        }
    })


    it(shouldPrint('Unauthorized', red(401)), async () => {
        try {
            const res = await test.get('/').set('Authorization', 'Bearer bad123token').send()
            res.should.exist
            res.should.have.status(401);
            res.should.have.json
            const { details, status } = JSON.parse(res.text)
            expect(details).to.equal('Unauthorized')
            expect(status).to.equal(401) 
        } catch (error) {
            throw error
        }
    })

})

 describe('Get token and ping', () => {
    let adminToken

    it(shouldPrint( 'Create custom jwt for testing', green(200)), async () => {
        try {
            const res = await test.get(`/createToken/${FIREBASE_UID}`).send()
            res.should.exist
            res.should.have.status(200);
            res.should.have.json
            adminToken = (JSON.parse(res.text)).token
        } catch (error) {
            throw error
        }
    })

    it(shouldPrint('PING', green(200)), async () => {
        try {
            const res = await test.get('/')
                .set('x-tenorders-testing', 'true')
                .set('Authorization', `Bearer ${adminToken}`).send()

            res.should.exist
            res.should.have.status(200);
            res.should.have.json
            const { details, status } = JSON.parse(res.text)
            expect(details).to.equal('PING')
            expect(status).to.equal(200)
        } catch (error) {
            throw error
        }   
    })

})
`

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