Pull to refresh

Comments 41

Как же я люблю дядю боба за эти притянутые за уши аналогии.
Но согласитесь, красиво же!
Сельхозпроизводители посчитали, что если кормить коров не сеном, а травой, они дают надоя молока на 30% больше, а жирность его становится выше на 10%. Из чего мы можем сделать вывод, что если кормить программистов печеньками с изюмом, они начнут больше писать кода, а их код станет более качественным.

Вот такая примерно логика получается.
Ну, у него поближе будет:

  • профессия только в начале становления
  • можно сделать результат работы качественней, что показывают некоторые примеры
  • требует дополнительных усилий
  • многие не верят, что эти усилия того стоят (я не о TDD, а о тестах вообще; не верится, что всего 14% не пишут тесты, как в результатах опроса)

К вашему примеру — если кормить программистов более качественной едой и заставлять их следить за здоровьем, то да — долгосрочно они будут писать код лучше и улыбаться больше. А печеньки — это как раз наоборот — переход с травы на сено. Но программисты — не коровы, более упрямые и верят в то, что только они знают как правильно ))
У нас тут с коллегой возник разговор, кто оптимист, а кто реалист. С одной стороны он надеялся, что использующих TDD будет побольше, я же сказал «Вау, ЦЕЛЫХ 3 человека всегда используеют TDD!». С другой — утверждает, что большая часть людей, проголосовавших за «Тестирую не по TDD», используют ручное тестирование — под отладчиком прогнали, ручками потыкали, и вперед. Я же искренне верю, что это пункт про автотестирование, просто тесты пишутся после кода, а не до.

Было бы классно разделять такие пункты на «пишу тесты, но не по TDD» и «Тестирую без автоматизации».
Аналогичная мысль возникла, когда увидел, что перекос в сторону этого ответа. Но, если сейчас добавить пункт опроса, будут некорректные данные.
Я проголосовал за "редко", т. к. использую TDD редко. Но при этом в других местах более часто использую написание тестов после кода.

Стоило разделить опрос на две части: использование/не использование автоматизированного тестирования и частота использования TDD.
Несколько моментов:
1. Рыбы второй свежести не бывает, но китайский ширпотреб одевает весь мир. Если речь идет о человеческих жизнях, требования на порядки выше. А прототипы можно и на коленке писать за 10 минут.
2. TDD — методология разработки, попытка переложить всю ответственность на этот процесс скучна. TDD тут не причем, просто бывает недостаточно требований со стороны заказчика или их проверки. С тем же успехом можно верить в то, что scrum поможет избежать приемочного тестирования.
TDD и ко как раз о том, как работать с имеющимися требованиями. Тесты и есть максимально формализованные требования. Да, могут быть ошибки на этапе формализации, да, могут быть ошибки в процессе проверки, да, может быть неописанное поведение, но в целом принцип "сначала тесты" как раз о том как максимально учесть имеющиеся требования в реализации и максимально обоснованно гарантировать, что в процессе реализации других требований, уже реализованные не сломались.
Я полностью согласен с тем, что TDD может выступать в роли формата общения между заказчиком и исполнителем, но, как показывает практика, TDD-подход слишком формализован для заказчика, потому что частота уточнений требований начинает зашкаливать.
А мой тезис в том, что дело не в TDD, а в конечной формализации требований и проверке результата, при этом как эти требования сформировались и проверились — не суть.
Тесты и есть максимально формализованные требования.

Это только в том случае, если тесты покрывают все классы эквивалентности входных параметров. Проблема в том, что циклометрическую сложность ненаписанного кода не посчитаешь.
Если в требованиях нет описания всех возможных случаев, то нет и требования покрывать их тестами и вообще как-то особо обрабатывать. Единственные ошибки тут могут быть на уровне понимания требований разработчиком, например, заказчик просит извлекать квадратный корень из значения, имея в виду и комплексные результаты, а разработчик при написании тестов о них не подумал, решив, что параметр всегда будет неотрицательный, поскольку в задаче обратного сказано не было. В любом случае в тестах будет зафиксировано требование, которое разработчик реализовал. Откуда оно взялось — отдельный вопрос, административный прежде всего.
Если в требованиях нет описания всех возможных случаев, то нет и требования покрывать их тестами и вообще как-то особо обрабатывать.
Классы эквивалентности меняются с реализацией. https://habrahabr.ru/company/mailru/blog/274771/ — п2.
Пардон, должно выглядеть так:

Если в требованиях нет описания всех возможных случаев, то нет и требования покрывать их тестами и вообще как-то особо обрабатывать.

Классы эквивалентности меняются с реализацией. https://habrahabr.ru/company/mailru/blog/274771/ — п2.
Аналогия про врачей ужасно натянута.
Не вижу как мытье рук может помешать врачам выполнять свои обязанности. К тому же, доказано, что они могут мыть руки.

Основная проблема TDD, что не все можно протестировать только по ТЗ, без реализации. Поэтому получается так, что сначала ты пишешь тесты, а потом их переделываешь.
Я при чтении у себя в голове заменял TDD на "тестирование", поэтому аналогия показалась удачной. Но в переводе так не напишешь, всё-таки статья не моя.
Почему дядя Боб в своей голове не провел подобный str_replace, статья стала бы гораздо более жизненной и гораздо менее дискусионной
Тесты пишутся для того кода, который в данный момент создается. Пишешь метод кольцевого буффера, значит в начале тест для него. Пишешь очередь, тесты вперед. А то, что потом из этих кусочков будет собираться программа которая должна соотвестовать ТЗ дело десятое.
Тесты нужно одновременно с кодом, тогда их будет легко создавать, они будут понятными и их влияние на код будет положительным.
И ненадо рассматривать модульные тесты как способ описания требований — они не для этого.
И при таком подходе аналогия с мытьём рук как раз вполне оправдана. Мытье рук позволяет избавится от вредных бактерий, и не занести их в организм. А TDD позволяет вовремя заметить и исправить неправильное (не ожидаемое) поведение отдельных модулей, таким образом программа будет собрана из модулей поведение которых было ожидаемым.
И уж никак TDD не гарантирует, что конечный продукт соотвествует ТЗ. Для этого есть функциональное и приемочное тестирования. TDD — это гигиена для программиста :)
ТDD — это когда по требованиям пишутся сначала тесты, а потом уже код.

А писать тест одновременно с написанием кода — это не TDD.
Вы не совсем правы. Вот что нам говорит о TDD Википедия

Разработка через тестирование (англ. test-driven development, TDD) — техника разработки программного обеспечения, которая основывается на повторении очень коротких циклов разработки: сначала пишется тест, покрывающий желаемое изменение, затем пишется код, который позволит пройти тест…
Именно это, на мой взгляд, и имел ввиду предыдущий оратор.
Спасибо, да это я и имел ввиду. Я тут пробежался по книге "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 изменилось, на то что я описал в коментарие выше.
Такой подход отлично подходит для различных простых/математических алгоримтов, которые понятно как писать.

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

При таком подходе, если заказчик изменит требования, то какая-то часть системы с тестами останется, ну а та часть что не вписывается в новые требования — выкидывается, вместе с тестами. В этом плане нет никаких отличий были тесты или нет. Ну, а если заказчик в начале хотел "машину", а потом передумал и заказал "подводную лодку", то выкидывать придется все.

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

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

В начале я придумать архитектуру решения, на этом этапе кода нет.

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

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

Тогда зачем я их писал?

TDD может неплохо заходить на ряде проектов, особенно таких, в которых встречаются типовые задачи, которые вы делаете с нуля, но существует ряд других проектов, в которых это не сработает.
Если вам нужны реальные кейсы, могу предоставить парочку. Если что, мне приходится писать расширения для Odoo. Но сути это не меняет. Такие же кейсы можно встретить в куче других систем и фреймворков, которые настолько огромные, что знать их полностью невозможно и часто встречаются такие проблемы (Django, Spring)
И первое незадокументированное поведение вашего фреймворка/языка/чего-то еще, с которым вы раньше не сталкивалась приведет вас к изменению архитектуры частично или полностью.

Возможно мы живем в разных мирах разработки ПО. Я пишу на C++ и пока были трудности только с реализацией какой-то "плюшки" в GUI, так как это потребует много времени от них отказывались, т.е. логику работы это не затрагивало. Правда для GUI тесты я не пишу и в ближайшее время не собираюсь.

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

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

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

Ну и по себе, могу сказать, что до какого-то момента я не понимал зачем тесты нужно — только лишняя трата времени. А потом что-то перевернулось и я увидел, что вот тут можно было написать тест так, а здесь так. А вот в этом месте тест очень пригодился… Но этот все личное :) и осознание этого ушло несколько лет и множество попыток начать писать тесты. Возможно и с тестами для UI будет то же самое :)

И самый важный момент — нельзя заставлять писать тесты, не важно, себя или кого-то другого. Тесты должны писаться по доброй воле, тогда они будут качественными и полезными. Так что не видите смысла писать тесты — не пишите, но иногда находите время подумать об этом и, возможно, в какой-то момент вы увидете, как можно протестировать то, что раньше казалось невозможным.
А в вашем случае, можно попробовать создать прототипы и когда станет понятно, что это будет работать, то переходить к разработке уже конечного решения с тестами. Я иногда такое делаю, но мне проще, так как для прототипа я выбираю что-то, что позволяет быстро проверить идею и проверки требуют какие-то локальные куски — какие-нибудь нюансы с GUI, работа с устройством (непосредственно протокол или интеграция с SDK от производителя) и т. п.

Проблема в том, что часто работа с фреймворкам + итерационная разработка размывает разницу между прототипами и готовым решением.

Если у меня уже есть готовый чат и мне нужно добавить к нему, скажем, broadcast, то чем готовое решение будет отличатся от прототипа?

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

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

а в некоторых случаях вредно (потому что после реализации нужно будет опять переписывать тесты)

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

А то что TDD не везде применимо — да, такое возможно, так как TDD применимо лишь там, где можно писать юнит тесты. Именно юнит тесты, а не функциональные или нагрузочные, они не имеют отношения к TDD. Если юнит тесты написать невозможно, то и TDD применить не получится.

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

Простите, если я ошибаюсь, но тесты нужно писать до реализации. И проблема именно в этом. Если вы пишите одновременно с реализацией — это не TDD.
С формулировкой о том, что работать "написал часть функционала — покрыл кодом" очень полезно, я категорически согласен.
У меня есть ощущение, что я что-то не так понимаю. Что я подразумеваю под созданием тестов в процессе реализации: предположим мне нужно реализовать некоторый класс.
Мои действия:
  1. пишу тест для конструктора
  2. реализую конструктор этого класса.
  3. разбираюсь что должен делать метод foo и пишу тест/тесты для него
  4. реализую метод foo
  5. разбираюсь что должен делать метод bar и пишу тест/тесты для него
  6. реализую метод bar

Это я считаю TDD.
Если я не ошибаюсь, то вы говорите про test-first.
В случае TDD вам нужно было бы сначала написать тесты для всей задачи и только потом приступить к реализации.
Вовсе нет. То, что описывает Hokum, вполне попадает под TDD.
Спасибо вам за интересную дискусию! Порывшись а гугле, я пришел к выводу, что обе наши точки зрения имеют место быть.
В общем случае Test-First Programming это техника программирования. В то время как Test-Driven Development (TDD) это методология разработки, которая использует TFP.
Кто-то считает, что TFP это когда заранее известен API, а дальше пишется тест для одного метода/функции, потом его реализация и т.д. А TDD когда заранее не известно как система будет спроектирована. Т.е. конечный API формируется в процессе реализации.
Как я это понял:
Если я заранее определил, что требуемую задачу я буду решать с использованием двух классов, где один будет отвечать за функциональность 1, другой за функциональность 2 и у них будут методы: метод 1.1, метод 1.2, метод 1.3 для класса 1 и метод 2.1, метод 2.2 для класса 2. Это будет TFP.
А если я заранее не знаю сколько классов и что они будут делать, и это решается в процессе, то это будет TDD.
Выжимка идей на stackoverflow.
А тут определяется TDD как TFP + continuous design.
И если я правильно понял вашу точку зрения, что по требованиям заказчика пишутся тесты, а потом начинается разработка, то это будет Acceptance test-driven development
И были мнения, что TFP и TDD просто разные названия одного и того же :)
UFO just landed and posted this here
а HTML как

http://symfony.com/doc/current/book/testing.html#your-first-functional-test

я не знаю, какая архитектура будет, она сто раз может поменятся

А как вы без тестов узнаете, что при изменении ничего важного не поломали?

Во время написания кода я просто смотрю, что возвращает функция с разными параметрами.

На таком Continous Integration не построишь
UFO just landed and posted this here
TDD "зашибись" потому, что врачи моют руки? Я правильно уловил мысль статьи? Не слишком ли они перегружена содержанием?
Не сомневайтесь, автор заявляет именно это. «TDD работает, потому что врачи моют руки». Надо себе футболку такую сделать.
По-моему, он говорит, что тестирование недооценено как раньше гигиена.
UFO just landed and posted this here
По личному опыту, проблемы TDD следующие:
Очень трудно внедрить TDD на legacy-проекте. Перед тем, как это сделать, придется существенно отрефакторить большую кучу кода, что чревато. Имеется в виду модификации старого кода или написание нового кода в интеграции или по аналогии со старым (единообразие часто бывает важнее красивости и каноничности).
— Через TDD неудобно писать сценарные тесты. Кто-то может возразить, что сценарные тесты должны быть интеграционными, а не unit-тестами — это предмет отдельного разговора. Важно то, что если мы хотим фиксировать сценарии и пишем для этого сценарные тесты, то делать это по TDD — значит, противоречить одному из его основных положений, — движению маленькими шагами. Я не говорю, что это невозможно, нет — это вполне возможно. Но неудобно.
— TDD не гарантирует качественной архитектуры (хотя некоторые фанаты почему-то думают иначе). Если не брать во внимание дальнейшую компонентную структуру системы, то все рискует развалиться, и никакой TDD не спасет.

Но я лично рекомендую всем, кто против TDD для каких-либо целей, попробовать его вначале на меньших масштабах и понять, когда его стоит использовать, а когда — нет. В частности, написание кода по TDD учит разработчиков думать о тестировании и API с другой стороны, и в дальнейшем их код становится намного лучше. Учиться нужно на практике.
Sign up to leave a comment.

Articles