Уважаемые читатели, разрешите признаться: я – сторонник разработки через тестирование (TDD). Ну вот, полагаю, что я только что потерял половину читателей, а вторая половина уже наготове: «Давай, выкладывай...». Действительно, разработка через тестирование – одна из тех вещей, которые вызывают либо обожание, либо ненависть. И если вы из тех людей, у кого на лице сейчас написан неподдельный интерес к TDD, то прошу позволения предложить вам еще несколько абзацев, который вам предстоит осилить (правда, два из них – всего по одной строчке). А если вы по-прежнему убеждены, что TDD – самая паскудная вещь на свете, если не считать, конечно, рыбьего жира, то можете просто заменить «TDD»названием любого технического процесса, который вам нравится, расслабиться и получать удовольствие. А можете заняться и чем-то еще, гораздо более интересным.
Первый опыт с разработкой через тестирование я приобрел в ходе знакомства с XP (то есть не с ужасной версией Винды, а с «экстремальным программированием»). Если быть точным – я читал книгу Кента Бека, уже ставшую классической. И вот там эта методика называлась «разработка, начинающаяся с тестирования» (test-firstdevelopment). Эта идея, как и многие другие, изложенные в книге, казалась мне совершенно бредовой. Нутром я чуял, что это не сработает просто потому, потому что это не сработает никогда. И именно поэтому сразу же решил с этой идеей повозиться. Почему, спросите вы? Может быть, потому, что я постоянно убеждаю слушателей, что бредовые идеи потому и называются бредовыми, что натурально не работают. А двойные стандарты – мой любимый грешок... И вот я решил взяться за проект (не боевой, разумеется; была у меня тогда одна шабашка), и поприменять такую разработку через тестирование – по крайней мере, пока не достигну первых результатов. Признаюсь честно: сам себя за это ненавидел. Ломаного гроша не давал за такую авантюру.
Так прошло две или три недели.
А потом, как всегда, внезапно, меня осенило. Ситуация была точно такая, как в те времена, когда я через «не могу» пытался освоить vi. Точно как при работе над моим первым проектом, на полпути к цели, мне открылся смысл контроля версий (в те давние времена она называлась «RCS»). Мне уже доводилось переживать такие откровения, когда я внезапно вкурил искусство обращения с таблицами, работая с Lotus 1-2-3 или (об этом – в другом посте), или когда я познал прелести ООП, обуздывая C++. И вот теперь я вполне активно пользуюсь разработкой через тестирование и даже пропагандирую ее в кругу коллег. Поверьте, эта практика действительно помогла мне выйти на новый качественный уровень программирования.
И все же мне нравится подчеркивать, что я не отношу себя к ярым поборникам TDD. В некоторых случаях TDD, безусловно, очень хороша, но этого не скажешь о любом проекте. Честно признаться, в последний раз я писал о разработке через тестирование довольно давно и даже обращал внимание читателей вот на эту статью, где отлично и взвешенно разъяснено, когда следует использовать TDD (и когда не следует). И действительно, прочитать эту статью стоит: она дает ответы на любые вопросы по данной теме, которые только могут возникнуть. Когда TDD уместна – вы ею пользуетесь, когда нет – не пользуетесь. Именно так, и никак иначе.
Но со временем мне стало казаться, что «уместна» такая методология значительно чаще, чем «неуместна». Приведу пример. Не так давно (месяца два назад) мне пришлось быстро заделать одну штуку, чтобы она заработала в кратчайшие сроки. Задача заключалась в том, что наш QA-отдел должен был протестировать кое-какой материал максимально оперативно – признаться, работа была не из легких. И вот мне пришлось написать код, который не планировалось вносить в итоговую базу – то есть он не пошел бы «в производство», и наши клиенты совершенно с ним не сталкивались бы. Код предназначался исключительно для использования в отделе тестирования. Функция его сводилась к мелкой возне с базой данных (причем даже не с базой данных именно того, с чем мы работали). Словом, чисто прикладная вещь, которую нужно было сварганить по-быстрому, только чтобы работала.
И вот тут мы сталкиваемся с одним из недостатков разработки через тестирование: оказывается, это слишком длительный процесс. Возникает вопрос: зачем использовать такую технику разработки (держим в уме: разработка через тестирование – это именно разработка, а не тестирование), которая только тормозит работу? Ну, есть, как минимум, две причины, по которым можно пойти на такое снижение скорости.
Во-первых, дело в том, что неизбежное замедление при TDD часто идет на пользу. Ведь все мы знаем, что иногда код не пишется, а просто бешено набивается. И вот, поработав так несколько часов, весь в мыле, вы обнаруживаете, что все сделали неправильно, и эту чушь лучше переписать, чем доделывать. И правда, разработка через тестирование в большинстве случаев (но не всегда) помогает не попадать в такие ситуации. Когда вам приходится начинать с написания тестов, вы, в сущности, вынуждены сразу продумывать интерфейс, а не теоретизировать. Проще говоря, вы самого начала пишете работающий (ну, на первый раз, может быть, и неработающий) прикладной код, сразу зная, с каким материалом он просто обязан взаимодействовать. Таким образом, вы сразу мыслите более четко и не попадаете на большинство тупиковых путей (хотя, опять же, случаются исключения).
Во-вторых, занимаясь разработкой программ через тестирование, вы действительно будете допускать меньше ошибок, чем раньше. Не то чтобы это можно было бы железно доказать, но исследования на практике пока подтверждают справедливость такого утверждения. Вы и сами в этом убедитесь, если сравните результаты работы до и после перехода на TDD. Но время, так или иначе, теряется – если не на исправление ошибок, то за счет замедления разработки. Стоит ли овчинка выделки? Пожалуй, да… почти всегда. А вот компенсируются ли при этом издержки неиспользованных возможностей, обусловленные тем, что вы вывели продукт на рынок позже, чем могли бы? Тут ситуация становится совсем тонкой, и я даже не уверен, что по этой деликатной проблеме проводились какие-либо исследования. Но зачастую, когда мне требуется решить, обойдусь я в данном случае без TDD или нет, я просто задаю себе вопрос: «мне нужно сделать быстро или хорошо?». Ни один из ответов не является априори верным – все зависит от ситуации.
Итак, в описанной выше ситуации я хотел справиться как можно быстрее – поэтому решил обойтись без разработки через тестирование. Но я забыл кое-что важное, о чем я часто говорю, проповедуя TDD. Видите ли, основное достоинство TDD заключается не в снижении количества ошибок и даже не в том, что эта методология заставляет начинать работу с создания интерфейса, уже на этапе разработки волшебным образом создает комплект тестов или позволяет отбрасывать некоторые безумные идеи прежде, чем вы потратите на них слишком много времени. Все это так, но основное достоинство разработки через тестирование – в ином.
С ней просто не страшно.
Чтобы пояснить этот момент, позвольте сделать небольшое отступление. Как-то раз мне довелось поучаствовать в рефакторинге базы кода, которой стукнуло без малого 10 лет. В ней было почти 2 миллиона строк кода. Чтобы справиться с такой задачей, в первую очередь нужно было убедить менеджеров, что нам придется замедлить разработку новых фич, чтобы освободить время для рефакторинга. В конце концов, если код стар, это еще не значит, что он плох, правда? Вот что написал однажды по этому поводу Джоэл Спольски:
Уверенность в том, что новый код лучше старого, абсурдна по определению. Старый код использовался. Он тестировался. Куча багов была найдена, и они были исправлены. С ним всё в порядке! В нём не появится ошибок только оттого, что он лежит у вас на винчестере. Всё как раз наоборот! Или, по-вашему, софт похож на старый москвич, ржавеющий от стояния в гараже? Или на плюшевого мишку, который уже далеко не так хорош, если он не совсем новый?» (русский перевод статьи «То, чего делать нельзя).
Как и многое из того, что пишет Спольски, это очень разумное замечание… но в нашем случае оно совершенно не выдерживает критики. Да, старый код плох, поскольку он напоминает механизм, склеенный скотчем и жвачкой, это Франкенштейн, который слеплен из «исправить это не было времени» и «а вот как это можно сделать лучше». В старом коде может быть три-четыре разных способа реализации объектов, два-три способа считывания и записи информации в базе данных, десятки запрограммированных ситуаций, которые никогда не возникают, и еще десятки таких, которые, казалось бы, возникнуть не должны – однако отнюдь! (Правда, лишь при крайне маловероятном стечении обстоятельств.) И что в результате? Страх. Вспомните, сколько раз вы брались исправлять что-то в подобном десятилетнем клубке проб и ошибок, а в итоге просто останавливались с мыслью: «Ох, возиться со всем этим – себе дороже». Сколько раз вы (или ваши менеджеры) решительно отказывались от планов довести до ума тот или иной код лишь потому, что игра не стоит свеч? Сколько раз вам приходилось пережить головомойку из-за того, что вы искренне пытались что-то поправить, но случайно допускали крайне неочевидную ошибку, которую не удавалось отследить на протяжении целых месяцев? Чему вас научили такие случаи?
Лучше не ввязываться в такие авантюры. И не рисковать понапрасну с оптимизацией. Пока не сломалось – не ремонтируем, и даже если сломалось – возможно, мы не отремонтируем, а только доломаем.
Оценочное (да и фактическое) время, необходимое на разработку, начинает увеличиваться. «Ох, этот кусок кода – тихий ужас, его так быстро не исправишь». Или: «Пожалуй, никто на свете уже не разберет этот код, нужно самому потратить время и во все вникнуть». И возникает порочный круг, замкнутый сам на себя. Просто нет времени, чтобы привести все в порядок, написать хорошую документацию, поскольку само внедрение новых функций теперь занимает гораздо больше времени, чем раньше.
TDD позволяет справиться со всеми этими проблемами. Вы больше не боитесь рефакторинга, так как уверены, что после рефакторинга весь код будет работать точно так же, как и раньше, поскольку все тесты так и будут выполняться. Если же сначала написать код, а потом – тесты, то тесты вполне могут выполняться до рефакторинга, но не после него. Я всегда подчёркиваю: если написать код, потом написать тест, потом запустить тест – и тест будет пройден, то это еще не значит, что код работает. Это означает, что либо код работает, либо тест написан неправильно – 50/50. Но при разработке через тестирование я уверен, что тест написан правильно (ведь он не работал до написания кода), и знаю, что код тоже написан правильно (поскольку тест сработал после написания кода). И можно спокойно заниматься любым рефакторингом – я уверен, что весь код работает правильно, особенно если я строго придерживался разработки через тестирование и никогда не писал код, не подготовив заранее заведомо неуспешный тест. Страх просто… испаряется.
И все же – гарантирует ли разработка через тестирование, что в вашем коде точно не будет ошибок? Нет, конечно же. Могут возникать взаимодействия между вашим и сторонним кодом – такие, которых никто не предвидел и не пытался тестировать. Также могут возникать случайные побочные эффекты, обусловленные тестовым кодом. Случаются и противоречия в спецификациях – в таких случаях тесты просто подтвердят, что вы правильно реализовали неправильную вещь. Существует масса деталей, в которых можно ошибиться. Но суть в том, что количество ошибок в коде радикально снижается, и ваша уверенность в правильности написанного кода достигает 90%. Это, конечно, не 100%. Но все же 90% – значительный прогресс по сравнению с нулем.
И на этот раз, увы, я предпочел позабыть обо всем этом – вернее, я посчитал, что все это неважно. Но оказалось, что моя маленькая задача – это на самом деле три тесно взаимосвязанные задачи. Потом я понял, что могу написать общую подструктуру для всех трёх, что эта подструктура как бы немножко сложновата (не слишком, но уж, во всяком случае, непроста). Вдруг ни с того, ни с сего я обнаружил, что пишу четыре или пять Moose-классов с графами зависимостей. Представляете, какой рефакторинг тут напрашивался? Но я не решился. Я уже слишком долго провозился с этой задачей и не хотел задерживать тестирование. А если бы при рефакторинге у меня что-то пошло не так, то, возможно, я бы об этом даже не узнал... В общем, меня стало напрягать даже само добавление новых фич, так как я решительно не мог гарантировать, что тот или иной модуль не поломает весь тот код, который я протестировал, и за который поручился.
В итоге мы закончили с этой задачей, сделали релиз. До сих пор никаких проблем вроде бы не возникло. Но я не берусь ничего гарантировать коллегам, которые решат поработать с нашим кодом. То есть, в коде, конечно, есть по-настоящему красивые фрагменты, и откомментирован он неплохо… но, при этом, хватает и таких мест, где только и остается спросить: «Чуваки, зачем вы это здесь прикрутили?». И что мне остается? Просто вздохнуть, и признаться: «Сказать по правде, просто побоялся здесь что-то менять, хотя, и не сказал об этом». Согласитесь, в такой ситуации вы вряд ли хотели бы услышать подобный ответ.
Думаю, в следующий раз я еще сто раз подумаю, а может, и правда стоит потратить время на разработку через тестирование. Потому что даже «быстрые и маленькие» задачи решаются лучше, если при этом ты уверен в каждом своем шаге.
ИСТОЧНИК
Читайте также
8 актуальных и интересных курсов по Rust (июнь 2023) + бонус от GitHub
8 актуальных и интересных курсов по Rust (июнь 2023) + бонус от GitHub
Рассмотрели преимущества и особенности языка Rust, а также сделали подборку курсов по нему, которые будут интересны как новичкам, так и опытным программистам.
7 комментариев
Какие курсы по тестированию пройти. Для новичков и специалистов (май, 2023)
Какие курсы по тестированию пройти. Для новичков и специалистов (май, 2023)
Профессия тестировщика стала одной из самых востребованных для входа в IT в последние несколько лет. Поэтому мы собрали эту подборку, чтобы вы знали, где на какие курсы тестировщика пойти в 2023 году и какую образовательную платформу выбрать для обучения.
7 отличных курсов по финансам. Уплыть «с галеры» и основать свой стартап
7 отличных курсов по финансам. Уплыть «с галеры» и основать свой стартап
Если вы посмотрели «Волк с Уолл-стрит» и хотите, как Леонардо ди Каприо прогуливаться по яхте с бокалом вина в руках, но не знаете, с чего начать, подборка курсов Digitaldefynd станет для вас отличным стартом. Здесь представлены как платные, так и бесплатные программы, которые помогут вам освоить финансовое моделирование. Они подойдут не только для начинающих слушателей, но и для экспертов.
Не Paint-ом единым. 13 курсов по UX/UI-дизайну для продвинутых и не только
Не Paint-ом единым. 13 курсов по UX/UI-дизайну для продвинутых и не только
Если вам нравится думать о том, как с минимальными затратами получить максимум эффективности, то проектирование пользовательских интерфейсов определенно вас заинтересует. DigitalDefynd сделал подборку курсов по UX/UI-дизайну как для новичков, так и для продвинутых специалистов.
Обсуждение
Релоцировались? Теперь вы можете комментировать без верификации аккаунта.