Я всегда ненавидел слово «ремонтопригодность» в контексте тестов. Тесты, как и любой другой код, обслуживаемы. Если не наступит время, когда мы решим, что больше не сможем его принять, а код нуждается в переписывании, код можно поддерживать. Мы можем пойти и изменить его, отредактировать или заменить.
То же самое касается тестов. Как только мы их напишем, они будут ремонтопригодными.
Так почему же мы говорим о ремонтопригодных тестах?
Проблема с тестами в том, что они не считаются «реальным» кодом. Они не производственный код.
Разработчики, начинающие путь к лучшему качеству, похоже, считают тесты не просто дополнительной работой, но и работой второго сорта. Все действия, которые не направлены на выполнение кода на рабочем сервере или клиентском компьютере, рассматриваются как «участники вспомогательных ролей».
Очевидно, что написание тестов связано с будущей стоимостью. Это затраты на вспомогательную работу, которая считается менее ценной.
Одной из причин, по которой разработчики боятся начинать писать тесты, является эффект накопленного множителя: «Хорошо, я готов написать тесты, которые удваивают мою рабочую нагрузку. Я знаю, что этот код изменится в будущем, и поэтому мне придется удвоить работу, много раз в будущем. Стоит ли оно того?»
Тестовое обслуживание стоит дорого
Но не обязательно из-за этого.
Первое изменение, которое нам нужно сделать, это ментальное. Мы должны понимать, что вся наша деятельность, включая «вспомогательную», является первоклассной. Это также включает в себя модификации тестов в будущем: в конце концов, если мы собираемся изменить код для поддержки этого требования, для этого потребуются тесты для этого требования.
Хитрость заключается в том, чтобы минимизировать усилие до минимума. И мы можем сделать это, потому что некоторые из этих будущих усилий — это отходы, которые мы создаем сейчас. Трата происходит, когда требования не меняются, но тесты не проходят, а не из-за ошибки. Затем нам нужно исправить тест, хотя реальной проблемы не было. Повторная работа.
Вот очень простой пример, взятый из поста атрибута Accuracy :
1
2
3
4
5
|
[Test] public void AddTwoPositiveNumbers_GetResult() { PositiveCalculator calculator = new PositiveCalculator(); Assert.That(calculator.Add( 2 , 2 ), Is.EqualTo( 4 )); } |
Что произойдет, если мы решим переименовать PositiveCalculator в Calculator ? Тест не скомпилируется. Нам нужно изменить тест, чтобы пройти.
Однако переименование не представляет особой проблемы — мы полагаемся на современные инструменты для замены различных случаев. Тем не менее, это очень зависит от инструментов и технологий. Если мы сделали это в C # или в Java, то есть не только автоматизация, но и механизмы быстрой обратной связи, которые это улавливают, и мы даже не думаем, что поддерживаем тесты.
Представьте, что вы получите ошибку компиляции только после 2 часов компиляции, а не сразу после внесения изменений. Или только после автоматизированного цикла сборки. Чем дальше мы получаем от автоматизации и быстрой обратной связи, мы склонны рассматривать обслуживание как более крупного монстра.
Снижение затрат на техническое обслуживание
Общий совет: «Не связывайте свои тесты с вашим кодом».
Я выбрал этот пример по одной причине: тесты всегда связаны с кодом. Уровень связи и используемые нами механизмы обратной связи влияют на масштабность этих задач по обслуживанию. Вот несколько советов по снижению вероятности проведения тестового обслуживания.
- Проверьте результаты, а не алгоритмы . Поскольку тесты связаны с кодом, чем меньше деталей реализации, о которых знает тест, тем лучше. Надежные тесты не полагаются на конкретные вызовы методов внутри кода. Вместо этого они рассматривают протестированную систему как черный ящик, даже если они могут знать, как она построена внутри. Эти тесты, кстати, также более читабельны .
- Работать против публичного интерфейса. Тестируйте снаружи и избегайте тестирования внутренних методов. Мы хотим сохранить внутренний список методов (и подпись) внутри нашего черного ящика. Если вы чувствуете, что это неизбежно, рассмотрите возможность извлечения внутреннего метода в новый открытый объект.
- Используйте минимальное количество утверждений. Чрезмерность в наших критериях утверждения, особенно при использовании проверки вызовов методов на зависимостях, может привести к срыву тестов без выгоды. Нужно ли знать, что метод вызывался 5 раз или чтобы он вызывался хотя бы один раз? Когда он вызывался, нужно ли знать точное значение его аргумента или, может быть, достаточно диапазона? С каждым уровнем специфичности мы добавляем возможности для прохождения теста. Помните, что мы с ошибками, мы хотим, чтобы информация помогла решить проблему. Если мы не получим дополнительную информацию из этих утверждений, снизьте критерии.
- Используйте хорошие инструменты рефакторинга. И хорошая IDE. И работать с языками, которые поддерживают их. В противном случае мы задерживаем отзывы об ошибках и приводим к росту стоимости обслуживания.
- Используйте меньше насмешек. Использование насмешек похоже на использование рентгеновских лучей. Они очень хороши в том, что делают, но передержка — это плохо. Издевательства связывают код с тестом еще больше. Они позволяют нам указать внутреннюю реализацию кода в тесте. Теперь мы полагаемся на внутренний алгоритм, который может измениться. И тогда нашему тесту понадобятся некоторые исправления.
- Избегайте рукописных издевательств. Написанные от руки являются худшими, потому что, если они не очень просты, очень легко скопировать поведение тестируемого кода в макеты. Фреймворки поощряют настройку поведения через интерфейс.
Есть поговорка: кодекс — это обязательство, а не актив. Тесты такие же — обслуживание не уйдет полностью. Но мы можем снизить стоимость, если будем придерживаться этих рекомендаций.
Ссылка: | Атрибут теста № 6 — Техническое обслуживание от нашего партнера JCG Джила Зильберфельда в блоге Geek Out of Water . |