Точно так же, как полный ответ на возникающие у вас проблемы, похоже, что вы не используете TDD очень долго, вы, возможно, не используете какие-либо инструменты, которые могут помочь с процессом TDD, и вы ставите больше значение в строке производственного кода, чем в строке проверочного кода.
Более конкретно для каждой точки:
1: TDD поощряет дизайн, который делает не больше или меньше того, что должен, он же «ЯГНИ» (вам это не понадобится). Это "сделай это свет". Вы должны сбалансировать это с «делай все правильно», то есть для включения в систему правильных концепций и шаблонов SOLID. Я придерживаюсь следующего правила: при первом использовании строки кода сделайте так, чтобы она работала. На второй ссылке на эту строку сделайте его читабельным. На третьем сделайте это ТВЕРДЫМ. Если строка кода используется только еще одной строкой кода, в то время не имеет особого смысла вставлять полностью твердотельный дизайн, разбивая код на абстрагированный от интерфейса класс, который можно подключить и поменять местами. из. Тем не менее, вы должны иметь дисциплину, чтобы вернуться и рефакторинг кода, как только он начинает получать другие применения. Дизайн TDD и Agile - это ВСЕ о рефакторинге. Вот загвоздка; как и в случае с водопадом, он просто стоит дороже, потому что вам нужно вернуться к этапу проектирования, чтобы внести изменения.
2: Опять же, это дисциплина. Принцип единой ответственности: объект должен делать одну конкретную вещь и быть единственным объектом в системе, который делает эту вещь. TDD не позволяет вам быть ленивым; это просто поможет вам узнать, где вы можете быть ленивым. Кроме того, если вам нужно создать много частичных макетов класса или много полнофункциональных полноценных макетов, вы, вероятно, неправильно проектируете объекты и тесты; ваши объекты слишком велики, у вашего SUT слишком много зависимостей и / или область вашего теста слишком широка.
3: Нет, это не так. Это требует, чтобы вы думали о том, что вам нужно, когда вы пишете свой набор тестов. Вот где действительно блестят помощники рефакторинга, такие как ReSharper (для MSVS); Alt + Enter - это ваш ярлык «сделай это». Допустим, вы TDDing нового класса, который будет записывать файл отчета. Первое, что вы делаете, это создаете новый экземпляр этого класса. «Подожди», - жалуется РеШарпер. «Я не могу найти этот класс!». «Так что создавай», говоришь ты, нажимая Alt + Enter. И это так; теперь у вас есть пустое определение класса. Теперь вы пишете вызов метода в своем тесте. «Подождите, - восклицает ReSharper, - этот метод не существует!», И вы говорите «затем создайте его», нажав Alt + Enter. Вы только что запрограммировали тестирование; у вас есть скелетная структура для вашей новой логики.
Теперь вам нужен файл для записи. Вы начинаете с ввода имени файла в виде строкового литерала, зная, что когда RS жалуется, вы можете просто сказать ему добавить параметр в определение метода. Подождите, это не модульный тест. Это требует метода, который вы создаете, чтобы прикоснуться к файловой системе, а затем вам нужно вернуть файл и пройти его, чтобы убедиться, что он правильный. Итак, вы решили вместо этого пройти поток; это позволяет вам передавать в MemoryStream, который идеально совместим с юнит-тестами. Есть , где TDD влияет на проектные решения; в этом случае решение состоит в том, чтобы сделать класс более твердым заранее, чтобы его можно было проверить. Это же решение дает вам гибкость для передачи данных куда угодно в будущем; в память, файл, по сети, именованный канал, что угодно.
4: Гибкая команда программирует по соглашению. Если нет соглашения, это блок; если команда заблокирована, код не должен быть написан. Чтобы решить блок, руководитель группы или руководитель проекта принимает командное решение. Это решение правильно, пока не доказано неправильно; если это заканчивается неправильно, это должно быть сделано быстро, чтобы команда могла двигаться в новом направлении, не возвращаясь назад. В вашем конкретном случае попросите вашего менеджера принять решение - Rhino, Moq и т. Д. - и исполните его. Все это будет на тысячу процентов лучше, чем тесты, написанные от руки.
5: Это должна быть реальная сила TDD.У вас есть класс;его логика - беспорядок, но он верен, и вы можете доказать это, запустив тесты.Теперь вы начинаете рефакторинг этого класса, чтобы сделать его более твердым.Если рефакторинг не изменяет внешний интерфейс объектов, тогда тесты даже не должны меняться;вы просто очищаете некоторую логику метода, о которой не заботятся тесты, кроме того, что она работает.Если вы действительно меняете интерфейс, то вы меняете тесты, чтобы делать разные вызовы.Это требует дисциплины;очень просто прогнать тест, который больше не работает, потому что тестируемый метод не существует.Но вы должны убедиться, что весь код в вашем объекте все еще выполняется надлежащим образом.В этом может помочь инструмент покрытия кода, который может быть интегрирован в процесс сборки CI и «сломать сборку», если покрытие не подходит для работы.Однако обратная сторона покрытия на самом деле двойная: во-первых, тест, который добавляет покрытие ради покрытия, бесполезен;каждый тест должен доказать, что код работает должным образом в какой-то новой ситуацииКроме того, «охват» не является «упражнением»;ваши тестовые наборы могут выполнять каждую строку кода в SUT, но они не могут доказать, что строка логики работает в любой ситуации.
Все это говорит о том, что был очень мощный урок в том, что TDD будет и выигрыватьне дал мне, когда я впервые узнал об этом.Это было кодовое додзё;задача состояла в том, чтобы написать синтаксический анализатор римских цифр, который бы взял строку римских цифр и вернул целое число.Если вы понимаете правила римских цифр, это легко спроектировать заранее и может пройти любой заданный тест.Однако дисциплина TDD может очень легко создать класс, который содержит словарь всех значений, указанных в тестах, и их целых чисел.Это случилось в нашем додзё.Вот загвоздка;если действительные заявленные требования синтаксического анализатора заключались в том, что он обрабатывал только те числа, которые мы тестировали, мы не сделали ничего плохого;система «работает», и мы не тратили время на разработку чего-то более сложного, которое работает в общем случае.Однако мы, новые агилиты, посмотрели на болото и сказали, что такой подход глуп;мы «знали», что это должно быть умнее и надежнее.Но разве мы?Это сила и слабость TDD;вы можете создать не более или менее систему, которая соответствует заявленным требованиям пользователя, потому что вы не должны (и часто не можете) писать код, который не соответствует или не подтверждает некоторые требования системы, предоставленные вам лицом, оплачивающимbills.
Несмотря на то, что я довольно часто пишу тесты после разработки, в этом есть серьезная проблема;Вы уже написали производственный код и, надеюсь, протестировали его каким-то другим способом.Если это не проходит ваш тест сейчас, кто не прав?Если это тест, то вы изменяете тест, чтобы утверждать, что то, что выводит программа в настоящее время, является правильным.Ну, это не очень полезно;Вы только что доказали, что система выводит то, что она всегда имеет.Если это SUT, то у вас есть большие проблемы;у вас есть объект, который вы уже полностью разработали, который не проходит ваш новый тест, и теперь вам нужно разорвать его и изменить материал, чтобы он это сделал.Если это ваш единственный автоматизированный тест этого объекта на сегодняшний день, кто знает, что вы сломаете, чтобы пройти этот тест?Вместо этого TDD вынуждает вас писать тест, прежде чем включать любую новую логику, которая пройдет этот тест, и в результате вы получите код, защищенный от регрессии;у вас есть набор тестов, которые доказывают, что код соответствует текущим требованиям, прежде чем вы начнете добавлять новые.Таким образом, если существующие тесты дают сбой при добавлении кода, вы что-то сломали, и вам не следует фиксировать этот код для выпуска, пока он не пройдет все тесты, которые уже были там, и все новые.
Если в ваших тестах есть конфликт, это блок.Допустим, у вас есть тест, который подтверждает, что данный метод возвращает X с данными A, B и C. Теперь у вас есть новое требование, и при разработке тестов вы обнаруживаете, что теперь тот же самый метод должен выводить Y, когда заданы A, B иC. Ну, предыдущий тест является неотъемлемой частью доказательства того, что система работала по-старому, поэтому изменение этого теста для подтверждения того, что теперь он возвращает Y, может нарушить другие тесты, построенные на этом поведении.Чтобы решить эту проблему, необходимо уточнить, что либо новое требование является изменением поведения по сравнению со старым, либо одно из поведений было неправильно выведено из требований приемки.