12 характеристик высокоэффективных тестов |
27.01.2021 00:00 |
Автор: Энди Найт (AndyKnight) Писать эффективные тесты трудно. Неустойчивые, непонятные и медленные тесты в целом бесполезны, потому что приносят больше вреда, чем пользы. Паттерн "Настрой – действуй – проверь" дает хорошую структуру, но какими же еще характеристиками должны обладать тест-кейсы? Ниже – 12 характеристик высокоэффективных тестов. #1. Внятные. По сути своей тест – это всего лишь пошаговая процедура. Он выполняет действия и проверяет результат. В каком-то смысле это живая спецификация – тест детализирует, как именно должна работать фича. Любому должно быть интуитивно понятно, как тест работает. Следуйте таким конвенциям, как "Настрой-Действуй-Проверь" или "Если – Когда – Тогда". Стремитесь к краткости без расплывчатости. Избегайте стен текста. Если вам сложно писать тест простым языком, пересмотрите дизайн тестируемой фичи. Если вы не можете его объяснить, как другие поймут, как ее использовать? #2. Уникальные Каждый тест в наборе должен покрывать уникальное поведение. Не повторяйтесь – тесты-дублеры с незначительными различиями дороги в поддержке и запуске, а толку от них немного. Если тест может покрыть несколько вариантов ввода, концентрируйтесь на одной вариации на класс эквивалентности. К примеру, эквивалентные классы для функции абсолютного значения могут быть положительным числом, отрицательным числом и нулем. Нет особой нужды покрывать множество отрицательных чисел, потому что функция абсолютного значения выполняет одинаковую операцию со всеми числами меньше нуля. #3. Индивидуальные Тестируйте что-то одно за один прием. Когда каждый тест концентрируется на одном определенном поведении, то их легче формулировать и автоматизировать. Они по природе своей понятны и поддерживаемы. Если такой тест падает, причина его падения очевидна. Каждый раз, когда вам хочется запихнуть в один тест несколько поведений, рассмотрите возможность разделения на несколько тестов. Проводите четкую грань между шагами "настройки" и "действия". Пишите как можно больше атомарных тестов. Избегайте "мировых турне". Я видел репозитории, тесты в которых занимают сто шагов и шляются по приложению, как Даша-путешественница. #4. Независимые Каждый тест должен быть независимым. Это означает, что каждый тест можно прогнать в одиночку. Каждый тест должен иметь свои рутины настройки и очистки, чтобы не причинить вреда и не оставить следов. Создавайте новые ресурсы для каждого теста. Автоматизированные тесты должны использовать паттерны вроде инъекции зависимости, а не глобальные переменные. Если один тест упадет, остальные должны продолжать проходить успешно. Независимость тест-кейсов – это краеугольный камень масштабируемых, поддающихся параллельному запуску тестов. Современные фреймворки тест-автоматизации делают большой упор на независимость тестов. Однако новички в автоматизации зачастую предполагают, что конец одного теста – это начало другого. Не пишите тесты так! Пишите тесты так, как будто каждый из них может запускаться самостоятельно, а порядок тестов в наборе может быть случайным. #5. Повторяемые Тестирование – деятельность повторов. Тест-наборы должны постоянно прогоняться, давая быструю обратную связь в ходе разработки. При каждом прогоне они должны давать однозначные результаты, потому что команда ожидает стабильности. К сожалению, ручные тесты сложно повторить. Они требуют длительного времени на прогон, а люди не могут проводить их в точности так же каждый раз. Тест-автоматизация делает тесты по-настоящему повторяемыми. Их можно автоматизировать один раз и непрерывно, одинаковым образом прогонять. Скрипты автоматизации тоже всегда срабатывают одинаково. #6. Надежные Тесты должны успешно пройти до завершения, неважно, возвращают ли они PASS или FAIL. "Нестабильные тесты" – тесты, периодически падающие из-за разных причин – тратят время и порождают сомнения. Если тест не может надежно прогоняться, как доверять его результатам? И зачем команда тратит столько времени на разработку тестов, если они плохо прогоняются? Для получения хороших результатов вы не должны перепрогонять тесты. Если они периодически падают, разберитесь, почему. Исправьте ошибки автоматизации. Настройте таймауты. Масштабируйте инфраструктуру до достаточных размеров. Выбирайте стабильность тестов, а не их скорость. И не пропустите хилые баги, которые могут скрываться в тестируемом продукте! #7. Эффективные Основная задача тестирования – это предоставление быстрой обратной связи. Быстрая обратная связь помогает командам ловить проблемы рано и продолжать безопасную разработку. Быстрые тесты позволяют получать быструю обратную связь. Медленные тесты дают медленную обратную связь и заставляют команду сокращать покрытие. Они тратят время и деньги, а также повышают риск, что баги нанесут больше ущерба. Оптимизируйте тесты, чтобы они были максимально эффективными не за счет их стабильности. Уберите ненужные шаги. Используйте умные ожидания, а не жесткие sleep. Пишите атомарные тесты, покрывающие конкретные поведения. К примеру, используйте API вместо UI для подготовки данных. Настройте тесты для параллельного запуска. Прогоняйте тесты в рамках непрерывной интеграции, чтобы немедленно получать результаты. #8. Организованные У эффективного теста внятный облик:
Облик дает представление о месте и типе теста. Убедитесь, что тесты находятся в правильных наборах. К примеру, тесты, взаимодействующие с веб-интерфейсом через SeleniumWebDriver, не должны сидеть в наборе юнит-тестов. Группируйте связанные тесты, используя субдиректории и/или тэги. #9. Фиксируемые Функциональные тесты выдают результат PASSили FAIL с логами, скриншотами и другими артефактами. Большие тест-наборы выдают множество результатов. Отчеты должны выдавать результаты в читабельном, подлежащем поиску формате. Они должны выделять падения цветом и сообщениями об ошибке. Они должны включать прочую полезную информацию вроде времени прогона и проценте успеха. Отчеты о юнит-тестах должны также давать информацию о покрытии кода. Публикуйте тест-отчеты на публичных досках, чтобы они были доступны всем. Большинство серверов непрерывной интеграции вроде Jenkinsвключают в себя механизм тест-отчетности. Более того, собирайте метрики вроде истории тест-результатов и времени прогона в формате данных, а не текстовых отчетов, чтобы можно было анализировать тренды. #10. Поддерживаемые Тесты по природе своей хрупки, потому что зависят от тестируемых фич. Если фичи меняются, тесты, скорее всего, сломаются. Более того, автотесты подвержены дупликации кода, так как часто повторяют одинаковые шаги. Дупликация кода – это его рак, так как проблемы плодятся во всей базе кода. Хрупкость и дупликация – это кошмар для поддержки. Чтобы снизить нагрузку на поддержку, разрабатывайте тесты при помощи тех же практик, что и при разработке ПО. Не повторяйтесь. Проще лучше, чем сложнее. Проводите тест-ревью. В автоматизации следуйте хорошим принципам дизайна вроде разделения интересов и построения слоев решения. Делайте так, чтобы тесты было легко потом обновить! #11. Достойные доверия Тест "успешен", если доходит до завершения и выдает корректный PASS/FAIL-результат. Правдивость результата очень важна. Тесты, сообщающие о ложноотрицательных результатах, заставляют команду бессмысленно тратить время на разбирательства. Тесты, дающие ложноположительные результаты, дают ложное чувство безопасности и позволяют багам ускользать. И первое, и второе приводит к потере доверия к тестам. К сожалению, я видал достаточно не заслуживающих доверия тестов. Иногда ассерты проверяют не то, что нужно, или вообще отсутствуют! Я также видел тесты, название которых не соответствует их содержанию. Эти проблемы обычно остаются незамеченными в больших тестовых наборах. Убедитесь, что все тесты до единого заслуживают доверия. Внимательно проверяйте новые тесты, и потратьте время на улучшение существующих тестов, в которых обнаружены проблемы. #12. Ценные Тестирование требует больших усилий. Оно крадет время у разработки чего-то нового, и поэтому должно стоить этих затрат. Так как покрыть все возможные поведения невозможно, надо применять стратегию на основе рисков, чтобы определить наиболее рискованные при падении сценарии. Затем нужно приоритезировать тестирование в пользу этих сценариев. Если вы не уверены, действительно ли тест приносит ценность, спросите себя – если этот тест упадет, будет ли команда тратить время на исправление бага? Если ответ "да", то тест очень ценен. Если ответ "нет", ищите другие, более важные сценарии, чтобы покрыть их тестами. Что-то еще? Эта дюжина признаков, безусловно, делает тесты очень эффективными. Однако это необязательно полный список. Есть ли у вас что добавить? Согласны ли вы с моим списком? |