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