Test Driven Development (TDD) – неоспоримо выдающаяся техника, дающая ряд преимуществ. Прикладные разработчики, причём вне зависимости от масштаба проекта и количества вовлеченных специалистов, в широкой массе не следуют TDD. Но есть и ярые сторонники такого подхода, причём они готовы апеллировать не только к здравому смыслу или производственной необходимости, но и на безоговорочно успешные примеры его внедрения. Одним из таких апологетов является Александр Люлин, который не только рассказывает о степени позитивного влияния TDD на разработку, но и делится экспертизой внедрения и неукоснительного каждодневного применения этой техники вплоть до исходных кодов и примеров сценариев (в своем блоге). Однако главный тормоз на пути следования принципам TDD стоит, что предсказуемо, набор чисто психологических установок. В режиме «жёсткого разговора» мы пообщаемся с Александром на тему необходимости Test Driven Development.
Test Driven Development – попытка следования моде? Слепое копирование чужих подходов?
Давайте «сразу определимся». Я не использую TDD в его классическом понимании. И не надо здесь цитировать «википедию»! Вообще, вряд ли кто-то из профессионалов рассматривает энциклопедические статьи в качестве руководства к действию. Мы свой подход «выстрадали» в рамках реализации успешного проекта, поэтому за нами реальный опыт, а не «тупое использование чужих идей». Скорее, мы используем синтез из TDD и собственных представлений о том, как нужно разрабатывать ПО. Даже если эти «внешние идеи» исходят от очень умных идей, их следует критически осмыслить и адаптировать к реальной компании, существующей команды и стратегии развития и обеспечения качества. Но я далее буду говорить «TDD», имея ввиду тот процесс “разработки через тестирования», который близко соотносится с энциклопедическим Test Driven Development, но идёт гораздо дальше него.
TDD — это попытка поставить всё с ног на голову. В классике инженерного дела сначала создаётся установка/техническая система/машина/агрегат. Конечно, методика испытаний имеется в виду при проектировании… но всё-так, программисты – это какой-то особый вид инженеров? Генетически ущербных? Сами себя таким не считаете?
Тут всё зависит от определения — что «голова», а что «ноги». И как известно самый научный спор — это спор о терминах. Если говорить о методики испытаний как самодостаточной и глубоко проработанной дисциплине, то нельзя сказать, что она есть в промышленных масштабах. Тогда бы она де-факто присутствовала везде и никакой полемики не было бы в принципе. Но это, собственно, не отличает нас принципиально от других инженеров, которые, например, делают кофеварки, газонокосилки или велосипеды (не путать с «велосипедами»).
Считаю ли я себя «генетически ущербным»? В силу профессии, отчасти, да. Поэтому и стремлюсь всеми силами стать лучше. Разработка ПО – инженерное ремесло, поэтому не надо вдруг себя считать суперменом или неким творцом. Мы – ремесленники (за исключением единиц), это нужно осознать и смириться. И попытаться стать нормальным (а не генетически ущербным) инженером – допуски, чертежи, методики испытаний и т.д.
TDD — сковывает инициативу разработчика. Уже не сам разработчик контролирует процесс, а задание «валится на него снеговыми комками»? Нет ли психологического дискомфорта? Программист у нас теперь не творец, а бездумная машина реализации им функционала?
Наоборот, TDD ничего не сковывает, но позволяет работать более продуктивно. Конечно, «продуктивно» означает «больше работать». Вместо одного класса приходится писать два: сам проектируемый класс и тест к нему. TDD это как раз для инициативных людей. Которые любят свою работу. Если есть желание оптимизировать (=сэкономить своё рабочее время), то это уже в дверь с надписью «для бездельников». Потенциальные бездельники или люди, практикующие безделье в рабочее время, могут дальше не читать.
Только никто не контролирует разработчика, он сам себя контролирует – «бьёт по рукам». Но это не мазохизм. Взамен разработчик получает кое-что важное – ощущение стабильности и уверенности в своём профессиональном завтрашнем дне. А как творец – он творит в два раза больше. Именно творит, а не «пашет». Пашет он как раз меньше. Жду следующего вопроса для разъяснений.
TDD — ставит во главу угла не качество реализации, а сиюминутные фичи. Как можно говорить о качестве кода? Об архитектуре системы? Мы на выходе с точки зрения программного кода получаем «салат оливье» — механическую смесь разных на вкус и цвет мелкопорезанных кусочков? Вы верите, что из винегрета, сложенный большой кучкой может символизировать собой архитектурное совершенно и конструктивное качество?
Вот это – совсем уж неправда. Именно качество и стабильность ставится во главу угла. Как раз, есть «фича» – есть тест, подтверждающий, что это «фича», а не «бага». Что не попало в ТЗ, то не попало в тесты, откуда тогда оно возьмётся в функционале? Только так: желание заказчика выражается в ТЗ, оттуда перетекает в тесты, и только потом появляется функционал.
Теперь про архитектуру… Архитектура это не есть что-то универсальное и самоценное. И не есть серебряная пуля. И не то, из чего делается плохая пуля. Я знаю архитектуру системы, т.к. я 15 лет ей занимаюсь и знаю великолепно, спасибо UML-чертежам. У меня полный набор проектных решений в виде UML-диаграмм. Но и здесь есть вероятность не удержать всё в памяти. Как всё это восстановить в памяти? Просто на конкретный тест посмотреть.
Тесты — это «архивированная память», а также «документация» и «варианты использования».
При этом тесты — более стабильны и инвариантны по сравнению с проектным кодом. Код перерождается. Эволюционирует. Умирает. С тестами это происходит гораздо реже. Видите, нет никакого конфликта интересов у архитектуры и тестов. И они в своей связке важнее кода.
TDD – некий бич, которым хлещут непокорных IT-рабов? Это что – некий вид «управы» на свободолюбцев, гордо именуемых «разработчиками»? Вы, разрабочтики, не должны думать. Ваша дело – тянуть лямку, выполнять план по валу! Смотри на тесты, данные тобой свыше, и не вздумай проявлять инициативу!
Значит так… «Руководству», (директорату и тем более заказчикам) реально нет дело, какие технологии используются (за редкими исключениями, на что мы наедятся не будем). Вообще, никого не парит, и что там «внутри» делают разработчики. Используют они RUP, TDD/ШмеДД, Agile/СуперАгиле или что-то иное…
Всем выше перечисленным «стейкхолдерам» – важен результат, а не процесс. Никакой здравомыслящий начальник не будет навязывать TDD. Ему нужен результат, а не раздражение разработчиков — «вот этот умник опять какие-то новомодные фишки нам навязывает». Умный начальник… реально умный (в IT это чаще, чем в других областях) может лишь сделать так, чтобы разработчики сами почувствовали эту необходимость. Это – единственный путь, иначе – тупик.
Нету никаких тестов, данных свыше. Тесты это — внутреннее дело разработчиков. И чем лучше тесты и чем их больше, тем разработчики «крепче спят». И тем меньше они вызываются на ковёр. Тесты – для разработчиков, не для начальников.
TDD – попытка некомпетентных и далёких от кодирования людей продвигать свою, чуждую разработчикам идеологию… своё, часто неправильное видение стратегии развития продукта.
Ещё раз. Тесты не диктует «директорат», руководство или заказчики. Тесты — это средство разработки. Тесты это – «внутренняя кухня» разработчиков и группы качества. Все остальные «стейкхолдеры» оперируют ТЗ. Ну или «экранами». Максимум… Тесты и «стратегия развития» вообще никак не соотносятся.
Допустим, меня обязали следовать технике TDD, хотя, согласитесь, очень многие люди комфортно чувствуют себя без этого. Ну и что это даёт по сравнению с нормальной техникой, когда «запланировали – реализовали – сделали тесты – протестировали»? Просто «красивая сказка» для начальника «мы используем передовые подходы?»
Можно же быть таким упёртым противником TDD! «Обязать» использовать тесты невозможно. Можно лишь уговорить. Если кто-то чувствует себя комфортно без тестов, то значит, что:
— его проект не такой сложный;
— он всё тестирует на реальных пользователях (не повезло им)
— он гений;
— он работает один.
Давайте, посмотрим в зеркало и выберем свою категорию.
Ещё раз: «Сказки» — нет, начальству – всё равно, ему нужен результат, сроки и качество.
Напахал я тестов. Начал под них (скрепя сердце) подгонять функциональный код. Выясняется… потом, что тест придуман неправильно. Я поработал впустую?
Вот «напахивать» ничего не надо. Одна фича – один тест. Итеративно. Фича порождает тест, а тест порождает фичу. XP, kiss-принцип.
А не бывает такого, что «тесты», которые приобрели вид «жёсткого задания», начали противоречить друг-другу?
Это мой любимый вопрос из разряда «гениальных»! Если тесты противоречат друг-другу, значит:
— ТЗ недостаточно проработано;
— ТЗ противоречиво.
Тут надо сказать «спасибо» тестам и вернутся к проработке ТЗ.
TDD – потеря времени. Давайте уж сначала напишем код, а потом будем его тестировать. Любое хождение «задом-наперёд», ненатуральный порядок разработки, даже если не сказывается на качестве, обязательно вызовет «тормоза».
Логично. Так многие и делают. Напишем код. Напишем. Покоммитим его в CVS/SVN. И что? Мы свою работу сделали? Типа «как повезёт»? Прокатит/не прокатит? Можно и так. Только потом тебя «поднимут с постели» и спросят «что за хрень ты тут написал?» Так?
Можно ведь по-другому — написать код и проверить его. А как мы его будем проверять? «Тупым» протыкиванием?
А если для «протыкивания» надо сделать 20-ть шагов? А если 100? А если «данных нет»? А если «смежники не готовы»? Не проще ли «написать тест» хотя бы для отладки? С придуманными «из головы» входом и выходом. По-моему, проще. А если тест УЖЕ написан и использовался для отладки, то почему не включить этот тест в «ночную сборку»?
TDD – способ поставить разработчика в унизительное положение. «Битый небитого везёт». Программист работает «от чувства вины» в перманентном состоянии нереализованности поставленной задачи.
Наоборот, у программиста есть всегда «реперные точки». Тут вот я написал тест. Он не проходит. Тут я реализовал функционал. Тест стал проходить.
Я всё покоммитил в CVS/SVN. Я свою работу СДЕЛАЛ и «могу спать спокойно».
TDD – нет ли ощущения, что программист всё время решает обратную задачу? Или разработчик – двоечник, подгоняющий решение под ответ задачи?
А программисты всегда решают обратную задачу. Ну или «строят сферического коня в вакууме». Программист – не двоечник, а обычный человек, который не может предусмотреть всё. Но зато то, что он предусмотрел – гарантировано работает. Есть и другие люди. Но они – гении, но их мало.
TDD – разработка в зазеркалье. Мы выполнили тесты, что не есть гарантия безошибочности. Кто отвечает за качество тестов?
Гарантии полной безошибочности" нет. Есть лишь гарантия того, что разработчик правильно понял ТЗ и реализовал его так, как понял.
TDD – телега впереди лошади. Лошадь постоянно тыкается мордой «в задний бампер», не видя ничего впереди себя. Как можно говорить о продумывании функционала в целом на уровне архитектуры системы, когда впереди – вдаль до самого горизонта – просто свалка потребностей?
Вот тут опять давайте вернёмся к «самому научному спору». Определим термины, что – телега, а что – лошадь. И не надо мне грозить википедией (как можно тестировать то, чего нет)?
Вот так будет правильно:
— «лошадь» — это ТЗ;
— «телега» — это КОД.
А что есть тесты? Тесты это — «оглобли» которыми лошадь привязана к телеге. Везёт весь процесс ТЗ, а тесты тянут за собой код и проверяют его соответствие ТЗ. Код едет в ту сторону, что и ТЗ. Нету ни TestFirst! Ни CodeFirst! Есть ТЗ-first. Но это если упрощённо. Цепочка выглядит так: ТЗ -> Набросок архитектуры -> Тест -> Код, а дальше возможны варианты.
ТЗ -> Набросок архитектуры -> Тест -> Код -> Тест -> Код
ТЗ -> Набросок архитектуры -> Тест -> Код -> Архитектура -> Тест -> Код
ТЗ -> Набросок архитектуры -> Тест -> Код -> Архитектура -> Тест -> Код -> ТЗ -> Архитектура -> Тест -> Код
…и т.д. и т.п.
First: ТЗ и набросок архитектуры, дальше начинается итерационная разработка.
И что особенно вкусно, что как только мы написали тест, нам не надо думать, а где проверять работоспособность нашего класса? Мы уже настроили всю инфраструктуру для его тестирования и проверки. Именно это и означает слово Driven в TDD.
Ладно, есть на свете «извращенцы». Тогда хоть как можно облегчить страдания? Какой правильный процесс при этом?
Не, ну спасибо за эпитет. Цепочку я описал выше. Если
Ладно, мы теперь не извращенцы, а правильные разработчики. Как впрячь коня (упрямого тестового пони на коротких ножках) и трепетную лань (мысль разработчика)?
Значит про «мысль». С разработчиком нужно тусоваться, поить пивом (шучу). Уговаривать. Развлекать. Сделать так, чтобы он тебе поверил. И чтобы считал твои мысли своими (привет Карнеги). По-другому — никак. Заставить – нельзя. Как и нельзя заставить писать хороший код. Можно подарить человеку книгу Мак-Конела на день Рождения. Но не более того. Никакие административные меры не помогут, а вызовут лишь озлобление.
Инструментарий, который мы используем:
— DUnit.
— Доработки к DUnit;
— Confluence;
— самописный аналог JIRA; в ту пору JIRA была ещё «сыра» поэтому мы её не взяли; сейчас — я думаю — взяли бы её;
— скриповая машина;
— FishEye (https://www.atlassian.com/software/fisheye/overview);
— Доработки к Confluence и FishEye позволяющие отслеживать связь коммитов, изменений кода и строящие привязку конкретных изменений кода к конкретным задачам;
— интеграция UML и Confluence;
Кто создает тесты? А если «роль» создателя тестов не предполагает знание программирования?
Тесты создают разработчики. Если речь идёт о новой функциональности.
Или тестировщики. Если речь идёт об ошибках.
И чаще всего, тестировщики пишут ошибку и сразу пишут к ней тест.
Тут кстати надо понимать одну простую вещь, ТЗ и ошибки в общем-то ничем не отличаются друг от друга принципиально. Только лишь «зоной ответственности».
«А если «роль» создателя тестов не предполагает знание программирования?» — на BDD или Fit-таблицы — мы пока не «замахивались», но мы работаем над этим.
TDD – это наше «всё» или Вы всё-таки признаёте ограниченность данной техники?
«Разруха она в головах, а не в багах». Нет ограниченности. Есть нежелание применять.
Ко всем ли системам применима техника TDD? Есть «особые случаи» или «другие подходы»?
Нет «особых случаев». Есть желание разработчиков «спать спокойно» и не думать лишнего, сохраняя нервы.
Есть что-то что можно выкинуть из TDD, есть что-то что можно привнести. Но в целом, TDD остаётся и гарантирует отсутствие паники. Panic-Driven Development исключается.
Что привело Вас к TDD?
Нужда. По мере роста сложности системы мы поняли, что «делаем одно», «отваливается другое». Тогда тесты стали необходимой составляющей. Мы к ним пришли эволюционно, но все к этому рано или поздно приходят. Также эволюционно перешли и к TDD. Внезапно оказалось. Что тесты не только полезно, но и удобно. Не надо инфраструктуру настраивать каждый раз. Написали тест, окружение для отладки готово, запускай этот тест хоть миллион раз. Так что кто уже «почувствовал» — welcome to TDD.
Что по-Вашему может быть лучше, чем TDD?
BDD. Есть люди в мире Delphi, которые этим занимаются и пропагандируют. Роман Янковский работает над этим работает. Ну и Fit-таблицы. Если есть желание развиваться, то можно до много самому дойти. Либо воспользоваться полунамёками, отрывочными статьями, википедией (в крайнем случае). Но сегодня я рассказал о реально работающем процессе. В деталях пишу в своём блоге, но общение с аудиторией доказало (выходя за рамки читателей), что главная проблема именно в понимании ценности TDD. Об этом сегодня попытался рассказать.
Ничего, что часть вопросов прозвучала как «троллинг»?
В жизни и не такое случалось, когда внедряли TDD в производственный процесс. Нормальная реакция.
Автор: VsevolodLeonov