Антипаттерны тестирования ПО. Часть 3

Антипаттерны тестирования ПО. Часть 3

https://habr.com/ru/post/358178/

Это часть 3.

Часть 1 читать тут https://telegra.ph/Antipatterny-testirovaniya-PO-CHast-1-07-13

Часть 2 читать тут https://telegra.ph/Antipatterny-testirovaniya-PO-CHast-2-07-15


Антипаттерн 10. Отказ писать тесты для новых багов из продакшна


Одна из задач тестирования — найти регрессии. Как мы видели в антипаттерне 4, в большинстве приложений есть «критическая» часть кода, где появляется большинство багов. Когда вы исправляете ошибку, то нужно убедиться, что она не повторится. Один из лучших способов гарантировать это — написать тест для исправления (либо юнит-тест, либо интеграционный, либо оба).


Ошибки, которые просачиваются в продакшн — идеальные кандидаты для написания тестов:


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


Я всегда поражаюсь, когда команды разработчиков (даже с солидной стратегией тестирования) не пишут тест на ошибку, найденную в продакшне. Они исправляют код и сразу исправляют ошибку. По какой-то странной причине многие разработчики предполагают, что написание тестов имеет значение только при добавлении новой функции.


Сложно представить что-то более далёкое от истины. Я бы даже сказал, что тесты, которые вытекают из реальных ошибок, более ценны, чем тесты, которые добавляются как часть новой разработки. В конце концов, вы никогда не знаете, как часто новая функция будет сбоить в производстве (возможно, она принадлежит некритическому коду, который никогда не будет сбоить). Соответствующий тест хорош, но его ценность сомнительна.


А вот тест, который вы пишете для реальной ошибки, очень ценный. Он не только проверяет правильность исправления, но и гарантирует, что оно всегда будет действовать, даже после рефакторинга в данной области.


Если вы присоединитесь к legacy-проекту без тестов, то это и самый очевидный способ начать внедрение полезного тестирования. Вместо того, чтобы пытаться угадать, какой код покрыть тестами, просто обратите внимание на существующие баги — и напишите тесты для них. Через некоторое время тесты охватят критическую часть кода, так как по определению все ваши тесты проверяют то, что часто сбоит. Одна из предложенных мною метрик отображает эти усилия.


Единственный случай, когда допустимо отказаться от теста — если ошибка в рабочей среде не связана с кодом и происходит из самой среды. Например, неправильную конфигурацию подсистемы балансировки нагрузки нельзя исправить с помощью юнит-теста.


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


Антипаттерн 11. Отношение к TDD как к религии


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


Вообще говоря, когда дело доходит до тестирования программного обеспечения:


  1. тесты можно писать перед соответствующим кодом;
  2. тесты можно писать одновременно с соответствующим кодом;
  3. тесты можно писать после соответствующего кода;
  4. можно вообще не писать тесты для конкретного кода.


Один из основных принципов TDD — всегда следовать варианту 1 (написание тестов перед кодом реализации). В целом это хорошая практика, но не всегда лучшая.


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


С практической точки зрения тому же стартапу рано слепо следовать TDD. Если вы работаете в стартапе, то ваш код может меняться так быстро, что TDD мало поможет. Вы можете даже отбросить код и начинать сначала, пока не напишете «правильный» вариант. Написание тестов после кода реализации — совершенно правильная стратегия в таком случае.


Отсутствие тестов вообще (вариант 4) тоже допустим. Как мы видели в антипаттерне 4, некоторый код вообще не нуждается в тестировании. Написание тестов для тривиального кода как «положено по TDD» ничего вам не даст.


Навязчивая идея апологетов TDD об обязательном написании сначала тестов нанесла огромный ущерб психическому здоровью здравомыслящих разработчиков. Об этой одержимости уже неоднократно говорили, так что надеюсь мне не нужно повторяться (поиск по ключевым словам «TDD дерьмо/глупо/мертво»).


Тут я хочу признаться, что несколько раз и сам работал по следующему сценарию:


  1. Сначала реализация основного компонента.
  2. Затем написание теста.
  3. Запуск теста — успешно.
  4. Комментирование критических частей кода компонента.
  5. Запуск теста — сбой.
  6. Удаление комментариев, возвращение кода в исходное состояние.
  7. Запуск теста — снова успех.
  8. Коммит.


Подводя итог, TDD — это хорошая идея, но не нужно постоянно следовать ей. Если вы работаете в компании из списка Fortune 500 с кучей бизнес-аналитиков и получаете чёткие спецификации, что конкретно нужно реализовать, тогда TDD может быть полезен.


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


Антипаттерн 12. Написание тестов без предварительного чтения документации


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


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


К сожалению, такая картина встречается слишком часто. Люди пишут несколько «вспомогательных функций» и «утилит» для тестов, не понимая, что во фреймворке эта функция либо встроена, либо подключается с помощью внешних модулей.


Такие утилиты затрудняют понимание тестов (особенно для джуниоров), поскольку наполнены «внутренними» знаниями, которые не распространяются на другие проекты/компании. Несколько раз я заменял «умные внутренние решения для тестирования» стандартными готовыми библиотеками, которые делают то же самое стандартизированным образом.


Следует потратить некоторое время и узнать о возможностях своего тестового фреймворка. Например, как он работает с:


  • параметризованными тестами;
  • имитациями и эмуляциями;
  • тестовыми настройками и демонтажом (teardown);
  • категоризацией текстов;
  • обусловленным выполнением тестов.


Если вы работаете над типичным веб-приложением, то следует произвести минимальное исследование и изучить лучшие практики в отношении:


  • генераторов тестовых данных;
  • клиентских HTTP-библиотек;
  • серверов для HTTP-имитации;
  • мутационного тестирования и фаззинга;
  • очистки/отката БД;
  • нагрузочного тестирования и так далее.


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


Антипаттерн 13. Плохое отношение к тестированию по незнанию


Хотя данный антипаттерн я упоминаю последним, но именно он заставил меня написать эту статью. Меня всегда разочаровывает, когда на конференциях и митапах я встречаю людей, которые «гордо» заявляют, что все тесты — пустая трата времени и что их приложение отлично работает вообще без тестов. Ещё чаще встречаются те, кто против определённого типа тестирования (обычно против модульных или интеграционных тестов), как мы видели в антипаттернах 1 или 2.


Когда я встречаю таких людей, то люблю расспрашивать их и узнавать истинные причины, стоящие за ненавистью к тестам. И всегда это сводится к антипаттернам. Или они работали в компаниях с медленными тестами (антипаттерн 7), или тестам требовался постоянный рефакторинг (антипаттерн 5). Их «задолбали» необоснованные требования покрыть тестами 100% кода (антипаттерн 6) или фанатики TDD (антипаттерн 11), которые пытались навязать всей команде собственное искажённое понимание TDD.


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


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


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

Report Page