Были ли у вас периодические или случайные сбои в ваших модульных тестах?

Я сделал это, и я рвал на себе волосы, пытаясь понять проблему. И я не одинок. Даже такие известные компании, как Google и Microsoft, борются с ненадежными тестами: одно исследование показало, что 41% и 26% тестовых целей, которые ранее прошли и не прошли хотя бы один раз соответственно.

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

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

Предыстория:

Работая над проектом внешнего интерфейса, я взял билет на техническое обслуживание для основных обновлений версии NodeJS и некоторых обновлений пакетов npm. Мы используем vanilla JS и пишем модульные тесты с помощью Jasmine и запускаем их с помощью Karma.

Проблема

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

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

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

Меня приветствовали следующие 5 прогонов тестов без никаких изменений между ними:

Запуск № 1: НЕУДАЧА! 2 теста не пройдены.
Запуск №2: УСПЕХ! Все 200 тестов пройдены.

Запуск № 3: НЕУДАЧА! 12 тестов провалены.

Запуск № 4: НЕУДАЧА! 1 тест завершился неудачно, но тестовый сценарий был преждевременно остановлен, а 75 тестов вообще не выполнялись.

Запуск № 5: УСПЕХ! Все 200 тестов пройдены.

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

Проверка работоспособности № 1 – изолируйте тест(ы) и наборы тестов, вызвавших сбой

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

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

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

#2 Отключить случайный порядок тестов

(ПРИМЕЧАНИЕ: если вы запускаете тесты в режиме наблюдения, вы должны вручную перезапустить скрипт, чтобы изменения в karma.conf вступили в силу).

В моем случае мне повезло, и это выявило несколько ошибок, поэтому я начал отлаживать их с помощью советов, упомянутых в шаге 5 ниже.

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

#3. Давайте получим исходные значения

Начальное значение — это то, что позволяет нам «зафиксировать» случайную последовательность выполнения теста.

В качестве аналогии вы можете представить себе книгу с 1000 страниц, пронумерованных от 1 до 1000, которые мы затем случайным образом меняем местами. Таким образом, в книге по-прежнему есть все 1000 страниц, но нумерация не соответствует тому, насколько глубоко в книге находится каждая страница.

Например, если мы откроем книгу и перелистнем 10 страниц, вместо страницы 10 мы окажемся на странице 72, следующая страница будет иметь номер 341, затем 290, затем 862 и т. д.

Исходное значение можно рассматривать как количество страниц, которые вы открываете в книге изначально. Таким образом, каждый раз, когда мы открываем книгу и переворачиваем страницу 10 раз (seed 10), мы получаем одну и ту же последовательность страниц [72, 341, 290, 862, …].

Вместо этого мы могли бы перевернуть 11 страниц (seed 11) и получить [341, 290, 862, …] или перевернуть X страниц и получить другую случайную последовательность, например [7, 899, 42, 605, …]

Используя начальные значения, у нас есть способ надежно провалить тест, что означает, что мы можем надежно исправить тест. Ура!

Чтобы получить эту информацию для нас с помощью Karma и Jasmine, нам нужно установить пакет npm и обновить наш файл karma.conf.

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

#4 Найдите неработающие начальные числа

С небольшим количеством bash мы можем запустить набор тестов 5–10+ раз с помощью одной команды, чтобы найти несколько плохих семян для начала отладки:

$ для я в {1..10}; делать карму начать ›› Karma_Log_Output.txt; сделанный

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

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

Однако в моем случае несколько трассировок стека не предоставили полезных стеков вызовов, вот что делать в этом случае:

#5. Пошаговая отладка с помощью отладчика Chrome

Условные точки останова:

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

Оглядываясь назад на стек вызовов Chrome:

Если вам нужно вернуться назад от точки остановки отладчика, вы можете использовать стек вызовов функций для проверки предыдущих вызовов функций.

Очистите стек вызовов с помощью списков игнорирования:

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

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

Что не сработало:

Теперь, когда я описал, что сработало и что я бы порекомендовал, я хотел бы упомянуть то, что я видел в рекомендациях других, против чего я хотел бы предостеречь: ручное исключение тестов/наборов.

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

Сводка

  • Тесты выполняются в произвольном порядке, и это идеальный сценарий, если только вы специально не отключили его для отладки.
  • Дважды проверьте, что тесты на самом деле периодически терпят неудачу, проверяя их изолированно с помощью `fit` и `fdescribe`.
  • Отключите случайное выполнение, чтобы попытаться добиться стабильного сбоя некоторых тестов.
  • После того, как вы исправите все сбои тестов, которые проявляются при последовательном выполнении тестов, начните последовательно получать некоторые начальные значения.
  • Используйте свои инструменты! Условные точки останова и построчная отладка с помощью инструментов Chrome Developer — ваши друзья.
  • Избегайте отключения тестов в качестве инструмента первой линии для исправления периодических сбоев тестов.

Я надеюсь, что вы нашли этот пост полезным, и в идеале вы предприняли усилия, чтобы исследовать и исправить любые периодические сбои тестирования в вашем проекте (ваши коллеги это оценят!).

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