В последнее время меня критикуют за то, что я связываю TDD с профессионализмом. Я признаю себя виновным и утверждаю, что связь существует.
Сразу скажу, что существует довольно много программистов, которых я уважаю, но которые либо не практикуют TDD, либо не считают, что это влияет на профессионализм. Например, Джим Коплиен (Коуп), Ричард Хикки и Дэвид Хайнемайер Хенссон (DHH).
- Как я уже говорил много раз ранее, Коуп является моим героем. Его труды в 90-е и позже оказали огромное влияние на мой образ мышления и на мою карьеру.
- Ричард Хикки является автором Clojure. Мы с ним общались несколько раз в 90-е, когда он ещё программировал на C++. Сейчас Clojure — это мой основной язык, и я стараюсь читать как можно больше трудов Хикки, и слушать как можно больше его докладов.
- DHH — это автор Rails; фреймворк, который сделал больше для сообщества Ruby, веб-сообщества и всей индустрии программного обеспечения, чем любой другой, который я могу вспомнить. Его влияние на меня и на отрасль в целом сложно переоценить.
Это хорошие, уважаемые, профессиональные программисты. Я использую их наработки. Я доверяю целостности их суждений. Мне повезло встретиться с ними и многому у них научиться. Они доказали свой профессионализм. Им вовсе не нужно моё одобрение.
Так как же я могу верить в то, что TDD ассоциируется с профессионализмом, когда есть профессиональные программисты, мнение которых я высоко ценю и которые не согласны с этой взаимосвязью?
Прежде всего, я не верю, что TDD является необходимым условием профессионализма. Во что я верю, так это в то, что он в настоящее время играет важную роль в профессиональном поведении. Я также считаю, что это будет играть всё большую роль в будущем.
Хочу рассказать историю о венгерском враче Игнаце Земмельвейсе, который в 1847 году достиг шестикратного снижения уровня смертности в своём родильном отделении просто заставив врачей мыть руки перед осмотром беременных женщин. Земмельвейс пытался убедить своих коллег из других больниц ввести мытьё рук как обязательную процедуру. Они сопротивлялись — более 60 лет. Причины их сопротивления звучат похоже на аргументы статей, которые утверждают в последнее время, что TDD мертв.
Врачи в то время не мыли руки. Они не видели никаких причин, чтобы мыть руки. Для них чистота и болезни были совершенно не связаны. Для нас, в 21-м веке, в это трудно поверить; но всего 167 лет назад было так же трудно поверить, что мытьё рук не было просто причудой.
Были ли эти врачи непрофессионалами? Конечно нет! Они работали как можно лучше с теми знаниями, которые у них были. Их недоверие к Земмельвейсу теперь кажется необоснованным, потому что мы сейчас знаем, что Земмельвейс был прав. Но сложно судить тех врачей, которые не верили каждому, кто приходил к ним со странными идеями. Прошло много времени и, к сожалению, случилось много страданий и смертей, прежде чем мытьё рук было принято в качестве обязательной медицинской процедуры.
Если наши врачи сегодня не будут мыть руки, мы назовём их непрофессионалами и потребуем их увольнения. Такое поведение недопустимо. Но тогда врачи, которые отказывали Земмельвейсу, были уважаемыми и профессионалами. Они не были злыми. Они даже не были глупыми. Их единственная вина состояла в том, что они были людьми.
Может показаться, что я хочу сказать, что я настолько же важен, как Земмельвейс. Я вовсе не хочу себе такого культового статуса. В конце концов, я могу быть неправ. TDD может не быть эквивалентом мытья рук, независимо от того, насколько сильно я в это верю. Если это так, то мои статьи и проповеди на эту тему пропадут в забавных сносках в статьях об истории программного обеспечения. Сносках, которые удалят лет через десять. В таком случае пусть так и будет.
Но пациенты умирают! Healthcare.gov. Knight Capital. Toyota. Этот список можно продолжать; и количество жертв увеличивается в прогрессии. Наше общество в целом становится все более и более зависимым от программного обеспечения, которое мы производим; и неудачи с каждым годом становятся всё более значительными.
Что-то должно измениться… или мы придём к огромной катастрофе.
Если я прав… Если TDD является столь значительным для индустрии программного обеспечения, как мытьё рук для медицины и убережёт нас от того, что мы все провалимся в тартарары, то Кент Бек будет провозглашен героем, и использование TDD будет нести всю тяжесть критерия профессионализма. После этого те, кто отказывается практиковать TDD, не будут приниматься в ряды профессиональных программистов. И меня не удивит, если в один прекрасный день TDD обяжут использовать по закону.
А теперь вы можете не согласиться со мной о TDD. Это нормально, и я готов обсуждать это. Но я не буду называть вас непрофессионалом, и я даже не буду думать, что вы непрофессионал. Потому что в данный момент в истории TDD не является необходимым критерием профессионализма, которым, я считаю, он когда-то станет.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Комментарии (40)
Meredian
21.03.2016 08:15+1У нас тут с коллегой возник разговор, кто оптимист, а кто реалист. С одной стороны он надеялся, что использующих TDD будет побольше, я же сказал «Вау, ЦЕЛЫХ 3 человека всегда используеют TDD!». С другой — утверждает, что большая часть людей, проголосовавших за «Тестирую не по TDD», используют ручное тестирование — под отладчиком прогнали, ручками потыкали, и вперед. Я же искренне верю, что это пункт про автотестирование, просто тесты пишутся после кода, а не до.
Было бы классно разделять такие пункты на «пишу тесты, но не по TDD» и «Тестирую без автоматизации».how
21.03.2016 08:35Аналогичная мысль возникла, когда увидел, что перекос в сторону этого ответа. Но, если сейчас добавить пункт опроса, будут некорректные данные.
grossws
21.03.2016 14:36Я проголосовал за "редко", т. к. использую TDD редко. Но при этом в других местах более часто использую написание тестов после кода.
Стоило разделить опрос на две части: использование/не использование автоматизированного тестирования и частота использования TDD.
arvitaly
21.03.2016 09:10+1Несколько моментов:
1. Рыбы второй свежести не бывает, но китайский ширпотреб одевает весь мир. Если речь идет о человеческих жизнях, требования на порядки выше. А прототипы можно и на коленке писать за 10 минут.
2. TDD — методология разработки, попытка переложить всю ответственность на этот процесс скучна. TDD тут не причем, просто бывает недостаточно требований со стороны заказчика или их проверки. С тем же успехом можно верить в то, что scrum поможет избежать приемочного тестирования.VolCh
21.03.2016 11:36TDD и ко как раз о том, как работать с имеющимися требованиями. Тесты и есть максимально формализованные требования. Да, могут быть ошибки на этапе формализации, да, могут быть ошибки в процессе проверки, да, может быть неописанное поведение, но в целом принцип "сначала тесты" как раз о том как максимально учесть имеющиеся требования в реализации и максимально обоснованно гарантировать, что в процессе реализации других требований, уже реализованные не сломались.
arvitaly
21.03.2016 17:08+1Я полностью согласен с тем, что TDD может выступать в роли формата общения между заказчиком и исполнителем, но, как показывает практика, TDD-подход слишком формализован для заказчика, потому что частота уточнений требований начинает зашкаливать.
А мой тезис в том, что дело не в TDD, а в конечной формализации требований и проверке результата, при этом как эти требования сформировались и проверились — не суть.
RPG18
21.03.2016 12:12Тесты и есть максимально формализованные требования.
Это только в том случае, если тесты покрывают все классы эквивалентности входных параметров. Проблема в том, что циклометрическую сложность ненаписанного кода не посчитаешь.VolCh
21.03.2016 12:46Если в требованиях нет описания всех возможных случаев, то нет и требования покрывать их тестами и вообще как-то особо обрабатывать. Единственные ошибки тут могут быть на уровне понимания требований разработчиком, например, заказчик просит извлекать квадратный корень из значения, имея в виду и комплексные результаты, а разработчик при написании тестов о них не подумал, решив, что параметр всегда будет неотрицательный, поскольку в задаче обратного сказано не было. В любом случае в тестах будет зафиксировано требование, которое разработчик реализовал. Откуда оно взялось — отдельный вопрос, административный прежде всего.
pushtaev
21.03.2016 15:10Если в требованиях нет описания всех возможных случаев, то нет и требования покрывать их тестами и вообще как-то особо обрабатывать.
Классы эквивалентности меняются с реализацией. https://habrahabr.ru/company/mailru/blog/274771/ — п2.pushtaev
21.03.2016 15:21Пардон, должно выглядеть так:
Если в требованиях нет описания всех возможных случаев, то нет и требования покрывать их тестами и вообще как-то особо обрабатывать.
Классы эквивалентности меняются с реализацией. https://habrahabr.ru/company/mailru/blog/274771/ — п2.
SirEdvin
21.03.2016 12:13Аналогия про врачей ужасно натянута.
Не вижу как мытье рук может помешать врачам выполнять свои обязанности. К тому же, доказано, что они могут мыть руки.
Основная проблема TDD, что не все можно протестировать только по ТЗ, без реализации. Поэтому получается так, что сначала ты пишешь тесты, а потом их переделываешь.how
21.03.2016 12:31Я при чтении у себя в голове заменял TDD на "тестирование", поэтому аналогия показалась удачной. Но в переводе так не напишешь, всё-таки статья не моя.
Davert
21.03.2016 21:05Почему дядя Боб в своей голове не провел подобный str_replace, статья стала бы гораздо более жизненной и гораздо менее дискусионной
Hokum
21.03.2016 12:42Тесты пишутся для того кода, который в данный момент создается. Пишешь метод кольцевого буффера, значит в начале тест для него. Пишешь очередь, тесты вперед. А то, что потом из этих кусочков будет собираться программа которая должна соотвестовать ТЗ дело десятое.
Тесты нужно одновременно с кодом, тогда их будет легко создавать, они будут понятными и их влияние на код будет положительным.
И ненадо рассматривать модульные тесты как способ описания требований — они не для этого.
И при таком подходе аналогия с мытьём рук как раз вполне оправдана. Мытье рук позволяет избавится от вредных бактерий, и не занести их в организм. А TDD позволяет вовремя заметить и исправить неправильное (не ожидаемое) поведение отдельных модулей, таким образом программа будет собрана из модулей поведение которых было ожидаемым.
И уж никак TDD не гарантирует, что конечный продукт соотвествует ТЗ. Для этого есть функциональное и приемочное тестирования. TDD — это гигиена для программиста :)SirEdvin
21.03.2016 13:39ТDD — это когда по требованиям пишутся сначала тесты, а потом уже код.
А писать тест одновременно с написанием кода — это не TDD.AlexNis
21.03.2016 20:25+1Вы не совсем правы. Вот что нам говорит о TDD Википедия
Разработка через тестирование (англ. test-driven development, TDD) — техника разработки программного обеспечения, которая основывается на повторении очень коротких циклов разработки: сначала пишется тест, покрывающий желаемое изменение, затем пишется код, который позволит пройти тест…
Именно это, на мой взгляд, и имел ввиду предыдущий оратор.Hokum
21.03.2016 21:10Спасибо, да это я и имел ввиду. Я тут пробежался по книге "Test Driven Development By Example", Kent Beck. Сообственно в ней есть фраза:
Which test should you pick next from the list? Pick a test that will teach you
something and that you are confident you can implement.
Я ее понимаю, что пишется тест для чего-то, что можно сразу реализовать.
По началу, я рассматривал TDD именно в контексте проверки на соответсвие ТЗ и мне это не нравилось. Но при очередном осмыслении для чего нужны юнит тесты у меня что-то "щелкнуло" и видение TDD изменилось, на то что я описал в коментарие выше.SirEdvin
21.03.2016 23:34Такой подход отлично подходит для различных простых/математических алгоримтов, которые понятно как писать.
А что делать с бизнес-логикой, особенно в случае, если вам приходится раз-два менять требования, потому что в них не учитывалось что-то важное и заказчик захотел полностью пересмотреть логику?Hokum
22.03.2016 00:23В начале я придумать архитектуру решения, на этом этапе кода нет. После того как архитектура придумана начинать реализовывать ее в коде начиная с самого ниженего уровня, как раз с самых простых алгоритмов, постепенно поднимаясь вверх.
Постепенно шаг за шагом из самых простых функций и классов складываются более сложные и для каждой функции, для каждого метода перед его реализацией писать тест. Соотвественно на каждой итерации будет понятно что и как писать.
При таком подходе, если заказчик изменит требования, то какая-то часть системы с тестами останется, ну а та часть что не вписывается в новые требования — выкидывается, вместе с тестами. В этом плане нет никаких отличий были тесты или нет. Ну, а если заказчик в начале хотел "машину", а потом передумал и заказал "подводную лодку", то выкидывать придется все.
Модульные тесты, которые пишет программист перед написанием кода, служат для тестирования этого кода, а не для описания конечной функциональности продукта. В конечном счете тесты будут написаны и для конечной функциональности, но когда до них дойдет дело уже будет понятно, как написать тест и как реализовать функциональность которая нужна.
Могу описать как решал тестовую задачу, если интересно.Необходимо реализовать дедупликацию данных на стороне сервера. Под сервером понимается исполняемый файл/библиотека, которую мог бы использовать веб-сервер. Работа с самими серверами, либо сетевая часть реализации не предполагается в рамках данного задания.
Функции которые библиотека должна экспортировать были оговорены в задании, но это особой роли не играет.
Формально получилась библиотека которая сохраняла данные в файлах, а если приложение перезапускалось, то данные терялись. Но изменение логики работы в моем варианте легко реализуемы.
SirEdvin
22.03.2016 02:21В начале я придумать архитектуру решения, на этом этапе кода нет.
И первое незадокументированное поведение вашего фреймворка/языка/чего-то еще, с которым вы раньше не сталкивалась приведет вас к изменению архитектуры частично или полностью.
Не скажу, что у меня прям так много опыта программирования, но я часто сталкиваюсь с такими проблемами, когда в конкретном месте ERP система не позволяет делать так. Это приводить к тому, что мне нужна новая архитектура решения. Получается, что все те тесты, которые я написал можно взять и выкинуть.
Тогда зачем я их писал?
TDD может неплохо заходить на ряде проектов, особенно таких, в которых встречаются типовые задачи, которые вы делаете с нуля, но существует ряд других проектов, в которых это не сработает.
Если вам нужны реальные кейсы, могу предоставить парочку. Если что, мне приходится писать расширения для Odoo. Но сути это не меняет. Такие же кейсы можно встретить в куче других систем и фреймворков, которые настолько огромные, что знать их полностью невозможно и часто встречаются такие проблемы (Django, Spring)Hokum
22.03.2016 07:48И первое незадокументированное поведение вашего фреймворка/языка/чего-то еще, с которым вы раньше не сталкивалась приведет вас к изменению архитектуры частично или полностью.
Возможно мы живем в разных мирах разработки ПО. Я пишу на C++ и пока были трудности только с реализацией какой-то "плюшки" в GUI, так как это потребует много времени от них отказывались, т.е. логику работы это не затрагивало. Правда для GUI тесты я не пишу и в ближайшее время не собираюсь.
но я часто сталкиваюсь с такими проблемами, когда в конкретном месте ERP система не позволяет делать так. Это приводить к тому, что мне нужна новая архитектура решения. Получается, что все те тесты, которые я написал можно взять и выкинуть.
А в вашем случае, можно попробовать создать прототипы и когда станет понятно, что это будет работать, то переходить к разработке уже конечного решения с тестами. Я иногда такое делаю, но мне проще, так как для прототипа я выбираю что-то, что позволяет быстро проверить идею и проверки требуют какие-то локальные куски — какие-нибудь нюансы с GUI, работа с устройством (непосредственно протокол или интеграция с SDK от производителя) и т. п.
Тесты помогают при рефакторинге, можно с большей увереностью говорить, что код после рефакторинга выполняет ту же функциональность, но опять же с оговорками, так как не под все нюансы могли быть написаны тесты. Тесты это не панацея и слепо доверяться существующим тестами нельзя. (Пожалуй это их самый большой недостаток — они могут вносить ложно чувство спокойствия — раз прошли тесты, значит я ничего не сломал :) Нужно понимать, что покрывают или нет существующие тесты вносимые изменения, в случае рефакторинга.)
Ну и по себе, могу сказать, что до какого-то момента я не понимал зачем тесты нужно — только лишняя трата времени. А потом что-то перевернулось и я увидел, что вот тут можно было написать тест так, а здесь так. А вот в этом месте тест очень пригодился… Но этот все личное :) и осознание этого ушло несколько лет и множество попыток начать писать тесты. Возможно и с тестами для UI будет то же самое :)
И самый важный момент — нельзя заставлять писать тесты, не важно, себя или кого-то другого. Тесты должны писаться по доброй воле, тогда они будут качественными и полезными. Так что не видите смысла писать тесты — не пишите, но иногда находите время подумать об этом и, возможно, в какой-то момент вы увидете, как можно протестировать то, что раньше казалось невозможным.SirEdvin
22.03.2016 10:11+1А в вашем случае, можно попробовать создать прототипы и когда станет понятно, что это будет работать, то переходить к разработке уже конечного решения с тестами. Я иногда такое делаю, но мне проще, так как для прототипа я выбираю что-то, что позволяет быстро проверить идею и проверки требуют какие-то локальные куски — какие-нибудь нюансы с GUI, работа с устройством (непосредственно протокол или интеграция с SDK от производителя) и т. п.
Проблема в том, что часто работа с фреймворкам + итерационная разработка размывает разницу между прототипами и готовым решением.
Если у меня уже есть готовый чат и мне нужно добавить к нему, скажем, broadcast, то чем готовое решение будет отличатся от прототипа?
Стоит заметить, я не говорю, что тесты не нужны, я говорю что TDD не всегда применимо и полезно, а в некоторых случаях вредно (потому что после реализации нужно будет опять переписывать тесты). Сравнивать это с мытьем руч у врачей неправильно.Hokum
22.03.2016 12:25Если у меня уже есть готовый чат и мне нужно добавить к нему, скажем, broadcast, то чем готовое решение будет отличатся от прототипа?
Прототип нужен чтобы проверить какую-то идею, возможность чего-то. В нем можно пренебречь различными проверками, обработкой каких-то ситуаций. Если сразу понятно что и как будет написано, то и прототип и не нужен. Прототип нужен, если заранее не известно удастся реализовать что-то используя фреймворк или нет. При этом функциональность которую вы прототипируете не обязательно должна интегрироваться с текущей рабочей системой.
а в некоторых случаях вредно (потому что после реализации нужно будет опять переписывать тесты)
А в чем вредность? Вот только переписывать не после реализации, а в процессе реализации. Да, придется потратить дополнительное время для их создания. Но вред от TDD может быть лишь, когда эта методология навязана и разработчик пишет тесты "для галочки". В этом случае у других разработчиков может сложится ошибочное впечатление, что по тестам можно понять как работает тот или иной метод, что имел ввиду автор этого кода и что на них можно полагаться при рефакторинге. Вот таких тестов лучше, чтобы не было.
А то что TDD не везде применимо — да, такое возможно, так как TDD применимо лишь там, где можно писать юнит тесты. Именно юнит тесты, а не функциональные или нагрузочные, они не имеют отношения к TDD. Если юнит тесты написать невозможно, то и TDD применить не получится.
И сравнивать с мытьем рук у врачей вполне можно, тем более автор сравнивает с конкретными врачами, которые работают в родильном отделении, а не обощает на всех врачей — не каждому врачу обязательно мыть руки перед каждым пациентом.
M-A-X
21.03.2016 13:35И какой смысл было переводить эту статью? :)
Сравнили, что ТДД так же важно, как и мытье рук. Круто.
Сам не использую ТДД.
Так как считаю, что в вебе оно трудно применимо. Функции можно протестировать, а HTML как?
И зачастую я не знаю, какая архитектура будет, она сто раз может поменятся.
Потом.
Во время написания кода я просто смотрю, что возвращает функция с разными параметрами.
Своего рода тоже ТДД, только не сохраненное в коде, а удаленное.how
21.03.2016 13:51-1а HTML как
http://symfony.com/doc/current/book/testing.html#your-first-functional-test
я не знаю, какая архитектура будет, она сто раз может поменятся
А как вы без тестов узнаете, что при изменении ничего важного не поломали?
Во время написания кода я просто смотрю, что возвращает функция с разными параметрами.
На таком Continous Integration не построишьM-A-X
21.03.2016 18:38+1А как вы без тестов узнаете, что при изменении ничего важного не поломали?
Так TDD предполагает, что тесты пишутся до кода.
Я не знаю, смогу ли я написать тесты, и держать все в голове, если оно все меняется.
Я сначала пишу легкий прототип, а потом по мере потребности расширяю/рефакторю его.
Есть другие виды тестов.
На таком Continous Integration не построишь
Ну да, это не автоматизировано. Но при желании можно все тесты сохранять.
На работе кое-какие части покрыты разными тестами.
В личных проектах тестов как таковых нет.
pushtaev
21.03.2016 14:38+1Да, статья — шаг вперед по сравнению с советами класть плитку вместо программирования всем, кто не использует TDD. Однако сообщество все еще ждет от евангелистов TDD стройной теории и ответов на вопросы и возражения.
Mixim333
21.03.2016 17:50На вопрос ответил: «тестирую, но не по TDD».
Как-то уже писал на Хабре, что стараюсь писать код так, чтобы зависимости были только «интерфейсными», т.е. класс «A», который использует класс «B», у меня понятия не имеет, как «B» подготавливает для него данные. Методы стараюсь писать такого объема, чтобы одним взглядом было понятно, что он делает (не более 70-100 строк) + даю понятное наименование, соответственно, если метод называется «dec» (декремент), а внутри идет: «return a+1;», то явно что-то не то.
Тесты, в основном, применяю для тех случаев, когда логика, построенная на основе тз хитрая и переделать ее просто невозможно или когда участок кода критически важен.
Davert
Как же я люблю дядю боба за эти притянутые за уши аналогии.
Rathil
Но согласитесь, красиво же!
Davert
Сельхозпроизводители посчитали, что если кормить коров не сеном, а травой, они дают надоя молока на 30% больше, а жирность его становится выше на 10%. Из чего мы можем сделать вывод, что если кормить программистов печеньками с изюмом, они начнут больше писать кода, а их код станет более качественным.
Вот такая примерно логика получается.
how
Ну, у него поближе будет:
К вашему примеру — если кормить программистов более качественной едой и заставлять их следить за здоровьем, то да — долгосрочно они будут писать код лучше и улыбаться больше. А печеньки — это как раз наоборот — переход с травы на сено. Но программисты — не коровы, более упрямые и верят в то, что только они знают как правильно ))