Ретроспективные уроки автоматизации: принцип изоляции тестов |
30.05.2019 00:00 |
Автор: Виктор Славчев (Viktor Slavchev) В этой части ретроспективных уроков автоматизации я продолжу рассуждать о принципах автоматизации и сконцентрируюсь на наиболее значимом из них (как минимум, по моему опыту) – на принципе изоляции тестов. Один из моих читателей сообщил, что информацию об этом принципе можно найти также по запросу "Герметичный шаблон тестирования". Узнать больше можно здесь: Hermetic servers – Google testing blog Что такое изоляция тестов?Честно говоря, я не знаком со словарным определением изоляции тестов – я вообще редко встречаю упоминания о нем, поэтому-то я и решил о нем написать. Могу только дать свое субъективное определение: Примечание: в контексте юнит-тестов изоляция тестов имеет другое значение – там принцип изоляции тестов подразумевает, что вы изолируете ваши юнит-тесты от всего внешнего, используя заглушки, имитаторы и другие инструменты. Сегодня мы сконцентрируемся на изоляции между тестами на интеграционном уровне и выше. Если вы не знакомы с уровнями тестирования – прочитайте предыдущую статью из этой серии. Изоляция тестов означает разработку и поддержку тестов, которые логически изолированы, то есть не зависят от любых других тестов, которые запускаются последовательно или параллельно с ними. Они также изолированы от окружения, специфических данных или конфигурации. Иными словами, все, что нужно тесту, он создает для себя самостоятельно. Возможно, это звучит самоочевидно, но если вы начнете писать код, то поразитесь, сколько предположений вы встраиваете в него, даже не осознавая этого. Изоляция тестов – это вариация одного из основных принципов разработки ПО: сильной связи и слабой зависимости. Сильная связь не относится к теме нашего разговора, но, возможно, вам будет любопытно прочитать эту статью – в ней подробно обсуждаются оба этих понятия. Слабая зависимость интересует нас куда больше. Это означает, что наш код (классы, методы, интерфейсы) должен быть независимым и ничего не знать о логике окружающих его классов, вызывающих его классов, расширяющих его классов, и т. п. Если мы намеренно или ненамеренно нарушаем это правило, мы начинаем создавать то, что называется "лапшой" – наши классы связаны друг с другом тоненькими ниточками, и как только мы меняем в них хоть что-то, это влечет за собой катастрофически непредсказуемые последствия. В тестировании изоляция тестов – это альтернатива слабой зависимости. Мы стремимся создавать тесты и тест-функции, которые ни от чего не зависят и не полагаются на другие тесты – в противном случае мы создадим логику, которая при определенных условиях станет очень шаткой. Хорошо изолированный тест не рассчитывает, что какой-то иной тест будет запущен или пройден – он работает сам по себе. Хорошие изолированные тесты дают одинаковые результаты при запуске:
Как узнать, что тест плохо изолированЕсть несколько критериев, описывающих плохо изолированный тест.
Техники для выявления плохой изоляции тестовНедавно я писал статью о случайном порядке запуска тестов с TestNG. Некоторые прогонщики тестов имеют встроенную функцию случайного запуска, а если ее там нет – ее можно добавить. Почему это полезно? При запуске тестов в случайном порядке вы увидите множество новых падений, потому что нарушатся ваши предположения о том, как они работают. Это значит, что при тест-дизайне и создании тестов мы зачастую ожидаем, что переменная уже задана и условие выполнено, но на самом деле этим должен заниматься наш тест или метод настройки.
Да, я именно об этом – прогоните ваш набор тестов сто раз подряд. Да, это небыстрая история, и, возможно, мало чем поможет вам, если в вашем наборе 10000 тестов или более, но мы и не собираемся заниматься этим ежечасно. Цель этой техники – выявить нестабильные или плохо спроектированные тесты, которые непостоянны в своих результатах. Сделать это легко – просто напишите скрипт, который вызывает ваш набор или тест-класс сто раз подряд. Чтобы оптимизировать его, я задаю условие отказа при первом падении – прогон завершен, как только будет замечено первое падение теста. На этом этапе нам все равно, сколько прогонов не пройдет. Даже если произойдет всего одно падение, проблема в наличии. Вам также понадобятся хорошие логи, потому что дебажить эти проблемы нелегко.
Другой тип цепочек или последовательностей, который мы встраиваем в код наших тестов – это зависимость от данных. Мы ожидаем, что эта запись уже есть в наличии, и мы можем получить ее или изменить ее. Эту проблему довольно легко выявить. Создайте свежую базу данных, с которой вы работаете, добавьте в нее новый набор данных (возможно, придется прогнать по ней несколько скриптов, если ваши данные – коммерческая тайна, или должны удовлетворять GDPR или аналогичным стандартам). Мне неоднократно приходилось переписывать тесты, созданные с учетом особого пользователя – я ожидал, что пользователь всегда будет в системе, или же использовал его, потому что этот пользователь удовлетворял определенным условиям, но все это очень плохой тест-дизайн.
Вот что еще может пригодиться вашим тестам в отношении окружения и конфигурации: в норме тест должен самостоятельно отвечать за создание условий, необходимых ему для запуска. В лучшем случае, если одной и той же логикой пользуются все тесты, это может быть вынесено в метод настройки, но этот метод должен выполняться каждый раз перед запуском теста. Будьте осторожны, не кладите туда бесполезный хлам, или создайте настройке хорошее наследование. Это краткий список техник и советов по улучшению изоляции и устранению зависимостей между вашими тестами. Это то, чем пользуюсь я – если у вас есть другие идеи, поделитесь ими! |