Javascript — отличный язык для асинхронного программирования. Но тестировать асинхронный код не всегда легко.
При модульном тестировании рекомендуется попытаться выполнить тестирование на максимально возможном уровне, сосредоточив внимание на тестировании API модуля с точки зрения внешнего использования/использования. Таким образом, мы гарантируем, что он делает то, что должен, но нас не волнует, как именно — практически сводится к нулю работа рефакторинга модуля. когда дело доходит до обновления тестов. Однако это часто увеличивает объем асинхронного тестирования.
В тех случаях, когда вызовы для тестирования являются прямыми обещаниями, вы ожидаете их и тестируете результат. Но это не всегда так. Например, когда необходимо использовать обратные вызовы, например, при подписке на шину сообщений, обещание ожидания отсутствует.
Первая попытка — с фиктивной задержкой
Обычный шаблон в этих случаях - произвольная задержка после проверенного вызова. В Javascript мы можем сначала определить простую функцию ожидания/задержки как удобную утилиту:
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
А затем используйте это, чтобы «убедиться», что мы получили опубликованное сообщение:
Однако у этого тривиального подхода есть пара проблем:
- Из-за этого тестирование занимает намного больше времени, чем необходимо (представьте, что выполняются сотни подобных тестов).
- Он делает предположения о производительности и планировании в среде, в которой выполняются тесты, что позволяет тестам фактически завершаться ошибкой, когда они не должны, в случае неудачного планирования и задержек в выполнении.
Вторая попытка — триггерPromise
Чтобы решить эту проблему, мы создаем служебную функцию, которую мы можем назвать triggerPromise
:
Вспомогательная функция возвращает новую функцию Promise
вместе с функцией resolve
для этого промиса. Давайте воспользуемся им и посмотрим, как он устраняет длительное произвольное ожидание в нашем тесте:
Теперь мы эффективно сигнализируем в точное время, когда мы получили сообщение, а также ожидаем этого сигнала, вместо того, чтобы угадывать и переоценивать.
Завершение всего этого гонкой против тайм-аута
Однако, если в тестируемом модуле есть какая-то ошибка в этой асинхронной функции, то мы не хотим застревать и ждать вечно, потому что это не будет хорошо работать с CI. Таким образом, следующей желаемой утилитой будет что-то вроде ждите этого, но с тайм-аутом. Поскольку у гораздо более используемого Promise.all
есть родственный элемент, а именно Promise.race
, мы могли бы использовать его следующим образом:
Так что теперь, вместо того, чтобы делать только await promise
в наших тестах, мы используем await asyncWithTimeout(promise)
, и вуаля — CI больше не останавливается из-за новых ошибок в нашем асинхронном поведении!
О да, нам нужен пример использования, завершающий наш небольшой асинхронный тест: