Принимает ли развитие, основанное на тестировании, внимание к дизайну?

У меня смешанные чувства по поводу TDD. Несмотря на то, что я верю в тестирование, у меня есть проблемы с идеей теста, который стимулирует мои усилия по разработке.

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

У меня проблема с вождением, а не с тестированием. Какие-нибудь мысли?

10 ответов

Нет.

Если все сделано правильно, Test Driven Development - ваш инструмент проектирования.

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

В предыдущем проекте разработчики использовали очень разрушительный шаблон синглтона, который навязывал зависимости по всему проекту, который просто нарушал все это при изменении требований:

TDD рассматривался как задача, тогда как его следует рассматривать как подход. [...]

Не удалось понять, что TDD - это не тесты, а дизайн. Буйный случай злоупотребления синглтоном в модульных тестах сделал это очевидным: вместо авторов теста, думающих: "WTF - это синглтон = значение; заявления в моих тестах? ", авторы тестов просто распространяли синглтон в тестах. 330 раз.

Печальным последствием является то, что тестирование на сервере сборки было выполнено, чтобы пройти, что бы это ни потребовалось.

Правильно выполненная тестовая разработка должна сделать разработчиков хорошо осведомленными о подводных камнях дизайна, таких как жесткая связь, нарушения DRY (не повторяйте себя), нарушения SRP (принцип единой ответственности) и т. Д.

Если вы пишете код прохождения для ваших тестов ради прохождения ваших тестов, вы уже потерпели неудачу: вы должны воспринимать сложные тесты как указатели, которые заставляют вас задаться вопросом: почему это делается таким образом? Почему я не могу проверить этот код без зависимости от какого-либо другого кода? Почему я не могу повторно использовать этот код? Почему этот код ломается, когда используется сам по себе?

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

Всегда есть риск переусердствовать с дизайном TDD или с предварительным дизайном. Таким образом, ответ заключается в том, что это зависит. Я предпочитаю начинать с пользовательского рассказа / приемочного теста, который является основой требования, которое мои тесты помогут в производстве. Только после этого я начинаю писать подробные модульные тесты в стиле TDD. Если вы разрабатываете и думаете только через TDD, то вы рискуете использовать подход "снизу вверх", который может дать вам единицы и классы, которые превосходно изолированы, но когда вы попытаетесь интегрировать их в пользовательскую историю, выполняющую задачу, вы может быть удивлен тем, что сделал все это неправильно. Для большего вдохновения смотрите BDD.

Большие "дебаты" об этом были зафиксированы между Робертом К. Мартином и Джеймсом Коплиеном, где первый является сторонником TDD, а второй заявил, что это разрушает дизайн системы. Вот что сказал Роберт о TDD и дизайне:

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

Джеймс Коплиен утверждает, что простое вождение вашего дизайна из TDD сопряжено с большим риском:

"Одна из вещей, которую мы видим во многих проектах, заключается в том, что проекты идут на юг со своим 3-м спринтом, и они терпят крах и горят, потому что они не могут идти дальше, потому что они загнали себя в угол архитектурно. И вы не можете Реорганизуйте свой выход из этого положения, потому что рефакторинг должен осуществляться по категориям классов, по иерархиям классов, и у вас больше не будет никаких гарантий относительно наличия той же функциональности ".

Также он приводит отличный пример того, как, вероятно, выглядел бы банковский счет, если бы вы тестировали его по сравнению с использованием ваших предварительных знаний для управления архитектурой:

"Я помню, когда я однажды разговаривал с Кентом, примерно в первые дни, когда он предлагал TDD, и это было в смысле YAGNI и делало самое простое, что могло бы сработать, и он сказал:" Хорошо. Давайте сделаем банковский счет, сберегательный счет. Что такое сберегательный счет? Это число, и вы можете добавить к числу, и вы можете вычесть из числа. Итак, что такое сберегательный счет, это калькулятор. Давайте сделаем калькулятор, и мы можем показать, что вы можете добавить к балансу и вычесть из баланса. Это самая простая вещь, которая могла бы работать, все остальное - эволюция этого.

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

Интересно то, что и сторонник TDD, и антагонист TDD говорят, что вам нужно разрабатывать заранее.

Если у вас есть время, посмотрите видео. Это отличная дискуссия между двумя влиятельными экспертами, и она длится всего 22 минуты.

Я полностью согласен с pjz. Не существует единого правильного способа разработки программного обеспечения. Если вы доведите TDD до крайности, не задумываясь ни о чем, кроме следующего юнит-теста, вы можете усложнить себе задачу. То же самое для человека, который отправляется на грандиозный программный проект, тратя месяцы на диаграммы и документацию, но без кода.

Умеренный. Если вы чувствуете желание составить быструю диаграмму, которая поможет вам визуализировать структуру вашего кода, сделайте это. Если вам нужны две страницы, возможно, пришло время начать писать код. И если вы хотите сделать это, прежде чем писать тесты, ну и что. Цель - работающее, качественное программное обеспечение, а не абсолютное соответствие какой-либо конкретной доктрине разработки программного обеспечения. Делайте то, что работает для вас и вашей команды. Найдите области, где улучшения могут быть сделаны. Итерация.

Здесь есть много неформальных мнений, в том числе популярное мнение (от Джона Лимджапа) о том, что плохие результаты возникают из-за неправильной работы, и утверждения, которые кажутся неподдерживаемыми лишь личным опытом. Эмпирические данные о преобладании и опубликованные результаты указывают в противоположном направлении от этого опыта.

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

Широкие эмпирические данные обосновывают теорию. Siniaalto и Abrahamsson, (Сравнительное исследование влияния разработки, основанной на тестах, на разработку программ и охват тестов), ESEM 2007, обнаружили, что "наши результаты показывают, что когезия может быть хуже (даже если Бек утверждает, что TDD производит очень когезивные системы"). В нашем втором исследовании мы заметили, что показатели сложности лучше с TDD, но показатели управления зависимостями были явно хуже ". Янзен и Саледиан (Действительно ли разработка, основанная на тестировании, действительно улучшает качество разработки программного обеспечения? IEEE Software 25 (2), март / апрель 2008 г., стр. 77 - 84) обнаружили, что "[T] результаты не подтверждают утверждения о низкой связи и повышенное сцепление с TDD ".

Обзор литературы раскроет другие публикации, продвигающие эти случаи.

Даже мой дорогой друг дядя Боб пишет: "Один из самых коварных и постоянных мифов гибкой разработки заключается в том, что предварительная архитектура и дизайн - это плохо; что вам никогда не следует тратить время на принятие архитектурных решений. Вместо этого вы должны развивать свою архитектуру. и дизайн из ничего, по одному тесту за раз. Извините, но это Конское Дерьмо." ("Скатология гибкой архитектуры", http://blog.objectmentor.com/articles/2009/04/25/the-scatology-of-agile-architecture)

Однако стоит отметить, что более широкой ошибкой является то, что люди думают, что это метод тестирования, а не метод проектирования. Ошеров указывает на множество подходов, которые часто приравнивают к TDD. Я не могу быть уверен, что подразумевается под плакатами здесь. См.: http://weblogs.asp.net/rosherove/archive/2007/10/08/the-various-meanings-of-tdd.aspx.

Я полностью согласен с вами по этому вопросу. На практике я думаю, что TDD часто оказывает очень негативное влияние на основание кода (дрянной дизайн, процедурный код, отсутствие инкапсуляции, производственный код изобилует тестовым кодом, интерфейсы повсеместно, трудно реорганизовать производственный код, потому что все тесно связано со многими тестами и т. Д.).

Джим Коплин некоторое время давал доклады именно на эту тему:

Недавние исследования (Siniaalto и Abrahamsson) TDD показывают, что он может не иметь преимуществ по сравнению с традиционной разработкой с последним тестированием и что в некоторых случаях это ухудшает код и что он имеет другие тревожные (их слово) эффекты. Больше всего меня беспокоит то, что это портит архитектуру. - блог Джима

Существует также обсуждение InfoQ между Робертом К. Мартином и Джеймсом Коплиеном, где они затрагивают эту тему.

Мой способ думать об этом - сначала напишите, как вы хотите, чтобы ваш код выглядел. Как только вы получите образец целевого кода (который сейчас ничего не делает), посмотрите, можете ли вы поместить на него тестовые леса. Если вы не можете этого сделать, выясните, почему вы не можете. В большинстве случаев это потому, что вы приняли плохое решение по дизайну (99%), однако, если это не так (1%), попробуйте следующее:

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

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

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

Есть три шага для завершения программного обеспечения:

  1. Сделай так, чтоб это работало
  2. Сделать это правильно
  3. Сделай это быстро

Тесты дают вам № 1. Ваш код не выполнен только потому, что тесты пройдены. Желательно, чтобы у вас было некоторое представление о структуре проекта (утилиты, объекты, к которым часто обращаются, слои, структура), прежде чем вы начнете писать свои тесты / код. После того, как вы написали свой код для прохождения тестов, вам необходимо переоценить его, чтобы увидеть, какие части могут быть реорганизованы для различных аспектов вашего приложения. Yuo может сделать это уверенно, потому что вы знаете, что, пока ваши тесты все еще проходят, ваш код все еще функционирует (или, по крайней мере, отвечает требованиям).

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

Я относительно новичок в TDD и модульном тестировании, но в двух сторонних проектах, в которых я его использовал, я обнаружил, что это скорее помощник по дизайну, чем альтернатива дизайну. Возможность независимо тестировать и проверять компоненты / подкомпоненты позволила мне быстро вносить изменения и опробовать новые идеи дизайна.

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

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

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

Тем не менее, часть более алгоритмической работы, с которой я заключил контракт в последнее время, немного пострадала от TDD без тщательного баланса дизайнерской мысли. Вышеприведенное утверждение о более безопасном рефакторинге все еще было большим преимуществом, но для некоторых алгоритмов TDD (хотя и полезно) недостаточно для того, чтобы найти идеальное решение. Возьмите сортировку в качестве простого примера. TDD может легко привести вас к неоптимальному (N^2) алгоритму (и масштабам прохождения тестов, которые позволяют вам быстро провести рефакторинг), как пузырьковая сортировка. TDD - это инструмент, очень хороший инструмент, но, как и многие вещи, его необходимо правильно использовать в контексте решаемой проблемы.

Это всегда баланс:
- слишком много TDD, и в итоге вы получите код, который работает, но над которым трудно работать.
- слишком много "основного кода, чистого дизайна и звуковой архитектуры", и вы в конечном итоге станете астронавтами, которые заговорили о параличе кодирования

Умеренность во всем.

Другие вопросы по тегам