Компоненты модульного тестирования с помощью имитационных сервисов

В моем текущем проекте мы запускаем множество сборок с использованием Jenkins: непрерывно и вручную. Обязательным шагом является утверждение, что все наши модульные тесты Jest проходят до того, как наше приложение Angular сможет развернуться в AWS. Этот процесс может занять несколько минут, и это может быть дефляция, когда вы запускаете сборку, принимаете ванную или перерыв на кофе, и возвращаетесь к неудачной сборке или, что еще хуже, к зависшей. Первое обычно более прямолинейно в отношении того, что пошло не так, но второе вызывало разочарование и замешательство.

Mocking Services

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

В тесте спецификации LoginService имитируется, но его функция getUserDataObservable не была переопределена. Похоже, это создает странную ситуацию внутри Jest. При запуске модульного теста мы получаем эту загадочную ошибку, и тест зависает на неопределенное время.

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

Этот конкретный тест спецификации легко решить, добавив фиктивную функцию getUserDataObservable в LoginServiceMock.

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

Вроде все нормально, пока статус RUNS не перестанет переключаться на PASS. Время истекает, но ошибки не возникает до тех пор, пока через минуту или около того не появится ужасная ошибка круглого объекта. Быстрый поиск этой проблемы в Google говорит нам, что какой-то объект ссылается на себя, но мы даже не знаем, к какому объекту это относится. Погрузившись глубже, мы наконец смогли найти решение этой загадки.

Параметры Jest

Благодатью спасения был флаг Jest под названием detectOpenHandles. Если вы посмотрите это в документации Jest, то увидите следующее описание:

--detectOpenHandles

Попытайтесь собрать и распечатать открытые ручки, препятствующие чистому выходу Jest. Используйте это в случаях, когда вам нужно использовать --forceExit, чтобы Jest мог выйти, чтобы потенциально отследить причину. Это подразумевает --runInBand, заставляя тесты запускаться последовательно. Реализовано с использованием async_hooks. Эта опция значительно снижает производительность и должна использоваться только для отладки.

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

После добавления этого в наш тестовый скрипт package.json

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

Эта ошибка была бесконечно более полезной, чем круглый объект, который мы видели в предыдущем прогоне.

Никогда в жизни я не был так счастлив увидеть трассировку стека.

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

Теперь, как рекомендовано, вам, вероятно, не следует использовать этот флаг чаще, чем нужно, но мы решили оставить его в наших сборках Jenkins. Даже с нашими 160 наборами тестов и почти 600 тестами мы не заметили серьезного снижения производительности. Преимущества обработки ошибок и время, сэкономленное за счет использования detectOpenHandles, того стоили.

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

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

Обновление от 04.08.2021

С момента написания этой статьи в моем проекте было 174 набора тестов и 2813 тестов. Мы были заняты! Я считаю, что мы достигли порога производительности от 1500 до 2000 тестов, где detectOpenHandles действительно имеет большое значение. Вот несколько значений времени работы для сравнения.

WITH detectOpenHandles - sequential
Test Suites: 174 passed, 174 total
Tests:       2813 passed, 2813 total
Snapshots:   0 total
Time:        1398.452 s
WITHOUT detectOpenHandles - parallel
Test Suites: 174 passed, 174 total
Tests:       2813 passed, 2813 total
Snapshots:   0 total
Time:        489.689s

По крайней мере, их документация точна. Я обновил нашу конфигурацию Jenkins, чтобы иметь возможность включать / выключать флаг.

Больше контента на plainenglish.io