Почему я не думаю, что TDD необходим

Почему я не думаю, что TDD необходим

23 ноября 2022 г.

Недавно я выступал с докладом об отладке для Лондонского сообщества Java. Во время части вопросов и ответов кто-то спросил меня о моем подходе к разработке через тестирование. Раньше я смотрел на эту практику в более позитивном свете. Написание большого количества тестов. Как это может быть плохо?

Но с течением времени я вижу это в другом свете.

Я рассматриваю его как очень ограниченный инструмент, который имеет очень специфические варианты использования. Он не вписывается в тот тип проектов, который я создаю, и часто мешает текущим процессам, которые должен продвигать. Но давайте отступим на секунду. Мне очень понравился этот пост, который разделяет типы и проблемы в TDD . Но давайте немного упростим, уточним, что каждый PR должен иметь хорошее освещение. Это не ТДД. Это просто хорошее программирование.

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

Хорошее

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

Есть и другие случаи, когда это имеет смысл. Когда мы создаем систему с очень четко определенными входными и выходными данными. Я сталкивался со многими такими случаями при создании курсов и материалов. При работе с реальными данными это иногда происходит, когда у нас есть промежуточное ПО, которое обрабатывает данные и выводит их в предопределенном формате.

Идея состоит в том, чтобы построить уравнение со скрытыми переменными в середине. Затем кодирование становится заполнением уравнения. Это очень удобно в таких случаях. Кодирование сводится к заполнению пробелов.

Плохое

<цитата>

«Разработка через тестирование ЯВЛЯЕТСЯ двойной бухгалтерией. Та же дисциплина. Те же рассуждения. Тот же результат». – дядя Боб Мартин

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

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

Большая претензия к TDD — это «его дизайн». Тесты фактически являются дизайном системы, и мы затем реализуем этот дизайн. Проблема в том, что мы также не можем отлаживать дизайн. В прошлом я работал над проектом для крупной японской компании. У этой компании был один из самых больших и подробных наборов книг по дизайну пристроек. На основе этих проектных спецификаций компания провела тысячи испытаний. Мы должны были пройти огромное количество тестов с нашей системой. Обратите внимание, что большинство из них даже не были автоматическими.

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

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

Хуже

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

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

В результате TDD уделяет слишком много внимания модульным тестам, которые «приятно иметь», а не основным интеграционным тестам. Да, вы должны иметь оба. Но у меня должны быть интеграционные тесты. Они не так хорошо вписываются в процесс TDD.

Правильное тестирование

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

Как я упоминал ранее, я проверяю покрытие только для интеграционных тестов. Мне нравятся юнит-тесты и мониторинг покрытия там, так как я тоже хочу там хорошее покрытие. Но для качества важны только интеграционные тесты. Для PR нужны юнит-тесты, мне все равно, если мы их написали до реализации. Мы должны судить о результатах.

Плохая автоматизация

Когда Tesla строила свои заводы по производству Model 3, она погрузилась в производственный ад. Источником проблем была их попытка все автоматизировать. принцип Парето идеально подходит для автоматизации. Некоторые вещи просто очень не поддаются автоматизации и делают весь процесс намного хуже.

Один момент, когда это действительно терпит неудачу, — это тестирование пользовательского интерфейса. Такие решения, как Selenium и другие, добились огромных успехов в тестировании веб-интерфейсов. Тем не менее, сложность огромна, а тесты очень хрупкие. В итоге мы получаем сложные в обслуживании тесты. Хуже того, нам сложнее рефакторить пользовательский интерфейс, потому что мы не хотим переписывать тесты.

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

Наконец-то

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

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

Это из-за моей предвзятости как разработчика Java, которому нравятся строгие языки с типобезопасностью. Такие языки, как JavaScript и Python, могут выиграть от большего объема тестов из-за их гибкости. Следовательно, TDD имеет больше смысла в таких средах.

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


Также опубликовано здесь


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE