Я только что купил Искусство модульного тестирования от Amazon. Я довольно серьезно отношусь к пониманию TDD, поэтому будьте уверены, что это настоящий вопрос.
Но я чувствую, что постоянно нахожусь на грани поиска оправдания, чтобы отказаться от него.
Я собираюсь сыграть здесь адвоката дьявола и попытаться сбить предполагаемые преимущества TDD в надежде, что кто-то может доказать, что я неправ, и помочь мне быть более уверенным в его достоинствах. Я думаю, что что-то упустил, но не могу понять, что.
1. TDD для уменьшения ошибок
В этом часто цитируемом сообщении в блоге говорится, что модульные тесты являются инструментами проектирования, а не для выявления ошибок:
По моему опыту, юнит-тесты не
эффективный способ найти ошибки или обнаружить регрессии.
...
TDD - надежный способ проектирования
программные компоненты («единицы»)
в интерактивном режиме, чтобы их поведение
уточняется через юнит-тесты.
Вот и все!
Имеет смысл. Крайние случаи все еще будут существовать, и вы найдете только поверхностные ошибки, которые вы обнаружите, как только запустите свое приложение. Вам по-прежнему необходимо провести надлежащее интеграционное тестирование после того, как вы построите хороший кусок своего программного обеспечения.
Достаточно справедливо, уменьшение количества ошибок - не единственное, с чем TDD должен помочь.
2. TDD как дизайнерская парадигма
Это, вероятно, большой. TDD - это парадигма дизайна, которая помогает вам (или заставляет вас) сделать ваш код более компонуемым .
Но способность к компоновке - это многократно реализуемое качество; функциональный стиль программирования, например, делает код вполне компоноваемым. Конечно, сложно написать крупномасштабное приложение полностью в функциональном стиле, но есть определенные компромиссные шаблоны, которым вы можете следовать, чтобы поддерживать компоновку.
Если вы начнете с модульной функциональной конструкции, а затем при необходимости аккуратно добавите в ваш код состояние и ввод-вывод, вы получите те же шаблоны, что и TDD.
Например, для выполнения бизнес-логики в базе данных код IO может быть изолирован в функции, которая выполняет «монадические» задачи доступа к базе данных и передачи ее в качестве аргумента функции, отвечающей за бизнес-логику. Это был бы функциональный способ сделать это.
Конечно, это немного неуклюже, поэтому вместо этого мы можем бросить подмножество кода ввода-вывода базы данных в класс и передать его объекту, содержащему соответствующую бизнес-логику. Это то же самое, адаптация функционального способа ведения дел, и это называется шаблоном хранилища.
Я знаю, что это, вероятно, принесет мне довольно плохую порку, но часто я не могу помочь, но чувствую, что TDD просто компенсирует некоторые вредные привычки, которые ООП может поощрить - те, которых можно избежать с небольшим количеством вдохновения от функционального стиля.
3. TDD как документация
TDD, как говорят, служит документацией, но он служит только документацией для пиров; потребитель все еще требует текстовую документацию.
Конечно, метод TDD может служить основой для примера кода, но тесты, как правило, содержат некоторую степень насмешек, которых не должно быть в примере кода, и, как правило, довольно изобретательны, чтобы их можно было оценить на равенство ожидаемый результат.
Хороший модульный тест описывает в сигнатуре метода точное поведение, которое проверяется, и тест проверяет не больше и не меньше, чем это поведение.
Так что, я бы сказал, ваше время могло бы быть лучше потрачено на подготовку документации. Черт, почему бы сначала не сделать только тщательную документацию и назвать ее «Управляемый документацией дизайн»?
4. TDD для регрессионного тестирования
В этом посте упоминается, что TDD не слишком полезен для выявления регрессий. Это, конечно, потому что неочевидные крайние случаи - это те, которые всегда портятся, когда вы изменяете некоторый код.
Что также следует отметить по этой теме, это то, что велика вероятность того, что большинство вашего кода будет оставаться неизменным в течение довольно длительного времени Итак, не имеет ли смысла писать модульные тесты по мере необходимости, всякий раз, когда код изменяется, сохраняя старый код и сравнивая его результаты с новыми функциями?