С чего начать изучение написания TDD — тестов?

Всем привет!

Бытует мнение, что, чтобы действительно повысить свои навыки и знания программиста - нужно писать TDD тесты.

Что это вообще такое и каких писать?
Приведите, пожалуйста, пример этих тестов как их пишут.

Спасибо.
  • Вопрос задан
  • 1200 просмотров
Решения вопроса 4
Fesor
@Fesor
Full-stack developer (Symfony, Angular)
нужно писать TDD тесты.

Нет, нет такой вещи как "TDD тесты". TDD это одна из методик экстремального программирования (XP). Вам уже привели ссылку на книгу Кента Бэка на эту тему (к слову крайне рекомендую)

Суть этой методологии в том, что бы разбить работу над кодом на три этапа, которые называются циклом красный-зеленый-рефакторинг.

- Красный - перед тем как написать код, мы должны написать тест который ломается (обычно в консоли сломанные тесты подсвечиваются красным). Согласно этой методологии писать код вы должны строго тогда, когда у вас есть сломанные тесты. Если сломанных тестов нет, то и код писать не нужно.
- Зеленый - когда вы получили красные тесты, вы должны максимально быстро дописать код так. что бы тесты были зелеными. Скажем если вы написали тест который ожидает от функции, что она вернет строку "foo" то в коде у вас должно быть не больше чем сама функция и вывод строки "foo". Как только мы этого добились мы либо рефакторим, либо добавляем еще красных тестов что бы потом дописать код. Конечно настолько примитивные вещи делать по такому циклу избыточно, и у Кента Бэка описывается понятие "длины шага", то есть сколько работы мы можем делать на каждом этапе. Вы всегда должны подключать здравый смысл словом.
- Рефакторинг - на предыдущих фазах мы не загонялись о том насколько наш код красив, насколько мы соблюдали принципы DRY и т.д. так что это фаза отчистки кода. Мы можем делать ее на каждой итерации, а можем раз в пару часов, но важно делать это как можно чаще. На этом этапе мы устраняем дублирование как в коде приложения так и в тестах. Важно отметить что хорошей мыслью будет не рефакторить одновременно и код и тесты, ибо у нас должен быть источник правды. Если мы почистили тесты и при этом они начали фэйлиться, то значит мы что-то сломали пока числити. И наоборот. А если менять и то и то между запусками тестов то не понятно кто виноват.

Обычно TDD практикуют используя unit-тесты (что логично, ибо они выполняются достаточно быстро что бы выполнение тестов не заставляло нас заваривать чай), что подразумевает собой то, что мы тестируем один юнит (один класс или объект), а все его зависимости должны подменяться на моки (фэйковые объекты, которые нужны что бы проверить как наш объект взаимодействует с другими, об этом тоже много написано). Но никто не запрещает использовать интеграционные/функциональные тесты и при этом практиковать TDD (так например делают чуваки практикующие BDD), а Кент Бэк это дело называет ATDD.

Собственно TDD дает нам следующие преимущества:
- вы не тратите время на проектирование системы в микроскопических масштабах, это эволюционный подход, архитектура приложения постоянно меняется и эволюционирует вместе с требованиями. Все требования формализуются в виде тестов.
- код всегда покрыт тестами (пусть и не на 100%, обычно хватает и 20% что бы можно было жить, все зависит от сроков жизни проекта и требуемого уровня надежности)
- если вам становится трудно писать тесты (например много зависимостей, сложно мокать) - то это должно навести вас на мысль о не правильной архитектуре и инициировать более глубокий рефакторинг. А при наличии тестов это не так уж и страшно.
- необходимость покрывать тесты увеличивает потребность в соблюдении всяких принципов типа SOLID и т.д. так как иначе мы начинаем писать тесты очень не эффективно и опять же возвращаемся к тому что с архитектурой что-то не так.

updated

тут в комментариях уличили в том что я не указал минусы и область использования методологии...

Минусы TDD проистекают из плюсов. Это эволюционный подход, который хорошо работает когда мы вносим изменения в систему маленькими порциями и всегда рефакторим наш код, что бы он большую часть времени был красивым и удобным к расширению. Если же вам в руки дали легаси проект и сказали отрефакторить, то TDD тут не подходит или подходит плохо. Но опять же такая задача ставится довольно редко, чаще - добавление функционала. И в этом случае мы возвращаемся к внесению изменений маленькими порциями и эволюционному подходу. Просто на это уйдет довольно много времени, но если сравнивать с "рефакторинг + добавление функционала + регрессионное тестирование" то в зависимости от ситуации TDD может дать как профит так и нет. Все зависит от сложности системы. На простых системах в этом нет смысла.

По поводу области применения... Тут есть несколько точек зрения. Как минимум TDD решает вопрос проектирования архитектуры, а не разработки алгоритмов. Этого мы достигаем тестами. Но опять же через юнит тестирование довольно не удобно разрабатывать определенные типы проектов: комиляторы, трансляторы, различные решения основанные на сложных алгоритмах (например алгоритмы сжатия, шифрования и т.д.), штуки завязанные на сетевом взаимодействии, например клиенты для протоколов. Для этих вещей больше подходят функциональные тесты или же их вовсе сложно покрыть тестами.
Ответ написан
POS_troi
@POS_troi
СадоМазо Админ, флудер, троль.
@Oxoron
Шарпер
Тут рецензия хорошей книги "Art of Unit Testing", плюс, несколько ссылок на доп-материалы. Книга не только расписывает плюсы-минусы тестов, но и объясняет, как лучше внедрять TDD в команды\проекты.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@abcd0x00
TDD на первый взгляд выглядит хорошо, но потом начинается свистопляска.

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

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

Это выглядит так:
Однажды ты хочешь написать классную программу, которая делает это, это и это. Ты, находясь под впечатлением от её возможностей (в воображении), продумываешь все её компомненты в голове до мельчайших подробностей и готов броситься в бой. Уже видишь эти данные, полученные из неё в результате, и уже лазишь по ним, применяя там-то и там-то.

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

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

Но это ещё не всё. Однажды наступает момент, когда программу надо не отрефакторить, а кардинально изменить. (А это самое важное - вовремя убрать какой-нибудь тупик, к которому всё идёт. Если этого не делать, программа будет представлять из себя конгломерат из затычек - и пример уже есть, десятой версии.) И становится просто жалко стирать тесты, которые к новой версии вообще не подходят.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы