Как TDD облегчает рефакторинг? - PullRequest
27 голосов
/ 01 ноября 2008

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

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

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

Наконец, будет ли хорошая книга по TDD и / или рефакторингу решать подобные проблемы? Если да, то что бы вы порекомендовали?

Ответы [ 8 ]

10 голосов
/ 01 ноября 2008

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

Когда вы изменяете функциональность класса или метода, естественно, что тесты тоже должны меняться. Фактически, следование TDD будет означать, что вы, конечно, поменяете тесты сначала . Если вам нужно изменить множество тестов, чтобы просто изменить один бит функциональности, это, как правило, означает, что большинство тестов переоценивают поведение - они тестируют больше, чем должны тестировать. Другая проблема может заключаться в том, что ответственность недостаточно хорошо заключена в вашем рабочем коде.

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

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

Я еще не читал ее, но слышал хорошие отзывы о книге "Тестовые шаблоны XUnit - рефакторинг тестового кода".

8 голосов
/ 03 ноября 2008

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

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

Являются ли главные архитектурные "изменения", происходящие действительно только рефакторингами? Если вы выполняете рефакторинг, хотя и резко, и тесты начинают проваливаться, это может указывать на то, что вы случайно изменили функциональность где-то. Это именно то, что модульные тесты должны помочь вам поймать. Если вы вносите радикальные изменения в функциональность и архитектуру одновременно, вы можете рассмотреть возможность замедления и перехода к этой красной / зеленой / канавке рефактора: никаких новых (или измененных) функций без дополнительных тестов и никаких изменений в функциональность (и тесты) при рефакторинге.

Обновление (на основе комментариев):

@ Cybis выдвинула интересное возражение против моего утверждения, что рефакторинг не должен нарушать тесты, потому что рефакторинг не должен изменять поведение. Он возражает, что рефакторинг меняет API и, следовательно, тестирует "break".

Во-первых, я бы посоветовал любому посетить каноническую ссылку на рефакторинг: Блики Мартина Фаулера . Только сейчас я рассмотрел это, и несколько вещей выскакивают из меня:

  • Меняется интерфейс рефакторинг? Мартин относится к рефакторинг как «сохраняющее поведение» изменение, которое означает, когда интерфейс / API меняется тогда все абоненты этого Интерфейс / API также должен измениться. Включая тесты, говорю я.
  • Это не значит, что поведение изменилось. Опять же Фаулер подчеркивает, что его Определение рефакторинга заключается в том, что изменения поведения сохранение .

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

Кроме того, я ожидаю, что тесты, даже модифицированные тесты, до продолжат проходить после завершения рефакторинга. Независимо от того, что тестировал этот тест (вероятно, утверждение (я) в этом тесте) должно оставаться действительным после проведения рефакторинга. В противном случае это красный флаг, который как-то изменял / регрессировал поведение во время рефакторинга.

Возможно, это утверждение звучит как бессмыслица, но подумайте об этом: мы ничего не думаем о перемещении блоков кода в базе производственного кода и ожидаем, что они продолжат работать в своем новом контексте (новый класс, сигнатура нового метода и т. Д.). Я чувствую то же самое в отношении теста: возможно, рефакторинг изменяет API, который должен вызывать тест, или класс, который тест должен использовать, но в конце точка теста не должна измениться из-за рефакторинга.

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

7 голосов
/ 01 ноября 2008

Основным преимуществом TDD для рефакторинга является то, что разработчик имеет больше смелости для изменения своего кода. С готовым модульным тестом разработчики решаются изменить код, а затем просто запустить его. Если полоса xUnit все еще зеленая, у них есть уверенность, чтобы идти вперед.

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

4 голосов
/ 01 ноября 2008

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

Затем вы пишете код, соответствующий тесту.

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

Красная полоса, Зеленая полоса, верно?

Рефакторинг Фаулера является ссылкой для рефакторинга, как ни странно.

Серия статей Скотта Амблера в Д-р. Dobb's («Ловкий край ??») - отличное прохождение TDD на практике.

3 голосов
/ 01 ноября 2008

изменение алгоритма на более эффективный, например.

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

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

2 голосов
/ 01 ноября 2008
1 голос
/ 01 ноября 2008

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

Цель тестов и спецификаций - определить правильное поведение системы. Итак, очень просто:

if definition of correctness changes
  change tests/specs
end

if definition of correctness does not change
  # no need to change tests/specs
  # though you still can for other reasons if you want/need
end

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

1 голос
/ 01 ноября 2008

Книга TDD Кента Бека.

Сначала проверьте. Следование принципам S.O.L.I.D ООП и использование хорошего инструмента рефакторинга необходимы, если не требуется.

...