Падай быстро. Ещё быстрее!
Одно из ключевых различий между кодом веб-приложения и кодом автоматизированных тестов в том, что код приложения выполняется как сервис, а тестовый код — как скрипт. Это важно, потому что такой подход подразумевает несколько структурных различий.
Весь автоматизированный тестовый код имеет следующую структуру:
- Setup — подготовка начального состояния, которое может быть общим для нескольких тестов.
- Arrange — достижение нужного состояния для начала изменения.
- Act — изменение состояния.
- Assert — проверка, изменилось ли состояние (как ожидалось).
- Teardown — возврат к состоянию до подготовки.
Для экономии ресурсов тесты не должны перехватывать исключения, которые возникают из-за ошибок. Обработка исключений в тестах — это сомнительная практика, и для неё есть лишь несколько оправданий:
- Известные состояния гонки, которые не являются результатом багов в приложении — например, AJAX-спиннер. Современные фреймворки уже решают это без исключений, но если вы ещё не перешли на них, то это допустимый способ. Но, серьёзно, переходите на современный фреймворк.
- Этапы Setup и Teardown — часто приходится перехватывать исключения, чтобы определить состояние данных.
- Более простая отладка, когда есть несколько исключений по разным причинам — в этом случае можно добавить сообщение и повторно вызвать исключение.
- Перехват ожидаемой ошибки, чтобы проверить её — это редко бывает оправдано для чего-либо, кроме юнит- или интеграционных тестов.
Автоматизированные тесты должны быстро завершаться с ошибкой. Они не должны тратить больше времени на сбой, чем на успешное выполнение. Они должны проверять только одно изменение состояния или одно взаимодействие компонента за раз. Это значит, что в них не должно быть нескольких проверок, и они не должны иметь несколько причин для сбоя — каждый тест должен всегда падать по одной-единственной причине: он нашёл дефект в фиче.
Хотя повторные попытки — это хороший способ сделать приложение устойчивым, в тестовой автоматизации они часто являются плохим признаком, и могут указывать на проблемы в приложении (или окружении). Хотя некоторая доля повторных попыток ожидаема в результате опроса состояния, многие популярные современные фреймворки уже поддерживают такую устойчивость «из коробки».
Тем не менее, тот факт, что ваши тесты перезапускаются, может быть индикатором проблем в приложении или окружении. Возможно, локальное окружение работает нестабильно из-за тестируемой системы или её зависимостей. Если автоматизатор вынужден делать массовые перезапуски тестов, это может указывать на возможные проблемы с пайплайном, инфраструктурой или с тестовыми данными. А может быть, в самом приложении есть баг, который не фиксят, потому что команда разработки не считает тестера полноценным членом команды.
Моя главная мысль здесь в том, что, встраивая устойчивость в тестовые фреймворки, автоматизаторы рискуют маскировать глубинные проблемы, которые были бы решены быстрее, если бы они были сразу видны команде.
То же самое касается и таймаутов. Хотя серверы и «пакетные» процессы используют их для повышения устойчивости, таймауты в тестовой автоматизации могут быть индикатором проблем в приложении и окружении, временных или постоянных. Более того, таймауты особенно опасны, потому что, однажды увеличив таймаут, автоматизатор редко возвращается чтобы проверить, нужен ли он ещё. В итоге он остаётся, добавляя время к каждому прогону теста.
Не прячьте проблемы, а используйте их
Возможно, это слишком очевидно, поэтому простите, если я кажусь слишком прямолинейным. Вместо того чтобы перехватывать ошибки, некоторые из ваших тестов или даже весь тестовый набор должны потенциально служить «воротами» для выполнения других тестов.
Например, если все ваши тесты должны логиниться на сайт, а функция логина не работает, ни один из ваших тестов не пройдёт. Нет смысла даже пытаться их запускать, потому что, если только у вашей компании не одно-единственное приложение, каждый запуск теста потребляет определённую часть общих ресурсов.
Большинство современных тестовых фреймворков имеют встроенный механизм пропуска тестов, который сделает именно это: предотвратит выполнение теста, если какой-то другой тест или шаг завершился сбоем. Хорошие автоматизаторы не только позволяют сбоям в своих тестах «всплывать на поверхность», чтобы выявить скрытую проблему, но и предсказывают, когда определённые сбои в одном тесте предвещают сбои в других тестах проекта.
Если в ваших тестах есть автоматические массовые повторные попытки (ретраи) для упавших тестов, вы можете использовать эту информацию для сбора метрик, а в сложных системах — даже для автоматических действий. Например, если вы повторяете прогон теста три раза, и он падает, потом проходит, потом снова падает, то поздравляю. У вас флаки-тест. Вы можете использовать эту информацию, чтобы автоматически отключить его и уведомить команду.
Но всякий раз, когда тест, который ранее падал, вдруг проходит, вам необходимо его изучить, если вы не уверены, что знаете причину такого сбоя. А если не уверены, лучше всего отключить этот тест.