Каковы недостатки тестового программирования?

47

Это все ярость в наши дни. «Каждый» рекомендует это. Это само по себе делает меня подозрительным.

Какие недостатки вы обнаружили при выполнении тестовой (управляемой тестом) разработки? Я ищу личный опыт от знающих практиков - я могу прочитать гипотетические размышления о сотне подражателей в других местах в Интернете.

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

Алекс Фейнман
источник

Ответы:

41

Их немало, но преимущества намного перевешивают недостатки.

Там крутая кривая обучения.

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

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

Вы делаете больше кодирования заранее.

Test-first означает, что вы не можете пропустить тесты (что хорошо) и означает, что в итоге вы напишете больше кода. Это значит больше времени. Опять же, вы не можете обойти это. Вы получаете вознаграждение за код, который легче поддерживать, расширять и, как правило, меньше ошибок, но это требует времени.

Может быть трудно продать менеджерам.

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

Может быть трудно продать коллегам-разработчикам.

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

В конце концов, преимущества перевешивают недостатки, но это не поможет, если вы просто проигнорируете недостатки. Знание того, с чем вы имеете дело с самого начала, помогает вам решить некоторые, если не все, недостатки.

Яко Преториус
источник
Это хорошие ответы, но может быть более конкретно о # 1? Мне особенно интересно услышать, как / удалось ли вам восстановить скорость программирования - что вы узнали, чего не знали, когда начали заниматься TDD?
Алекс Фейнман
Обновлено, чтобы дать некоторые разъяснения
Жак Преториус
7
Если вы сейчас занимаетесь тестированием, общее время, потраченное на разработку, не должно существенно измениться. Похоже, что все занимает больше времени, потому что вы выставляете время, необходимое для написания и поддержки модульных тестов.
ChrisF
1
@JeffO ты знаком с "Я собираюсь написать себе минивэн!" школа кодирования?
Алекс Фейнман
1
@tvanfosson - потому что они пытаются изменить две вещи одновременно - как начало тестирования, так и TDD - что может быть проблематично. Это также добавляет к оценкам времени - совершенно правильно - так что менеджеры и клиенты видят только предварительное увеличение, а не то, что общее время фактически известно (за один раз) и может даже быть меньше. Если они проводят какое-то тестирование, тогда это увеличение будет меньше.
ChrisF
35

Test-first предполагает, что вы пишете код, который:

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

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

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

Примеры:

Мой личный лучший пример - при написании кода безопасности для asp.net. Если они предназначены для работы в агрессивной среде из конфигурации компьютера, они помечены, подписаны и опечатаны, и поскольку они работают с объектами бога IIS, их очень трудно правильно высмеять. Добавьте некоторые ограничения для производительности и использования памяти, и вы очень быстро потеряете гибкость в использовании объектов-заполнителей в оставшихся областях.

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

Билл
источник
Можете ли вы привести некоторые контрпримеры? Когда я не буду писать что-то, что можно тестировать в режиме юнит-теста? Почему бы мне не писать код, который можно было бы смоделировать или ввести (кроме унаследованного кода, который сам по себе является темой)?
Алекс Фейнман
отредактировано, чтобы добавить раздел примеров
Билл
4
Согласовано. Работа TDD, похоже, основана на ряде предположений о машинах, с которыми вы работаете; похоже, это не относится к 50% моих проектов.
Пол Натан
Я полностью согласен ... Отличный ответ
Хелбен
2
Это похоже на что-то в этой игре - подходит для многих ситуаций, не подходит для других. Остерегайтесь тех, кто защищает Единый истинный путь в любой области разработки программного обеспечения.
Алан Б
25

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

Хороший инженер находит компромиссы и применяет правильный баланс, когда / где / как применять тест в первую очередь. Кроме того, если вы обнаружите, что постоянно тратите гораздо больше времени на разработку тестов вместо реального кода (в 2-3 раза или более), у вас проблемы.

Другими словами, будьте прагматичны и разумны с TDD (или с чем-то еще в разработке программного обеспечения).

luis.espinal
источник
Может быть, это то, откуда возникло «новое» определение устаревшего кода Майкла Перса (то есть «Код без тестов»)?
Фил В.
Это определение не сработало бы для меня :) Для меня любой код, который выполняется в рабочей среде и подлежит изменению, является устаревшим кодом, независимо от качества кода или теста. Обычно мы связываем «устаревший код» с «плохим кодом» или «устаревшим кодом», когда в действительности плохой код и устаревший код уже присутствуют в разрабатываемом коде, который еще не использовался в рабочей среде. Наша цель должна состоять в том, чтобы наш код был унаследован с самого начала, и чтобы он был настолько качественным и полезным, что он будет использоваться в течение многих лет, десятилетий вперед.
luis.espinal
6

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

Однако есть две проблемы, которые довольно заметны.

Набор тестов должен быть сохранен

Если вы серьезно относитесь к TDD, вы в конечном итоге будете писать множество тестов. Более того, требуется время и опыт, чтобы понять, какова правильная гранулярность тестов (перебор почти такой же плохой, как и перебор). Эти тесты также являются кодом , и они чувствительны к битроту. Это означает, что вы должны поддерживать их как все остальное: обновлять их при обновлении библиотек, от которых они зависят, время от времени реорганизовывать ... Когда вы вносите большие изменения в свой код, многие тесты внезапно устаревают или даже просто неправильно. Если вам повезет, вы можете просто удалить их, но во многих случаях вам придется извлекать полезные фрагменты и адаптировать их к новой архитектуре.

Тестирование абстракций время от времени

Мы используем Django, у которого довольно хорошая среда тестирования. Однако иногда он делает предположения, которые немного расходятся с реальностью. Например, некоторые промежуточные программы могут нарушать тесты. Или, некоторые тесты делают предположения о бэкэнде кэширования. Кроме того, если вы используете «настоящий» БД (не SQLite3), то подготовка БД для тестов займет много времени. Конечно, вы можете (и должны) использовать SQLite3 и базу данных в памяти для тестов, которые вы делаете локально, но некоторый код будет вести себя по-разному в зависимости от базы данных, которую вы используете. Настройка сервера непрерывной интеграции, работающего в реалистичной конфигурации, является обязательной.

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

Все это говорит о том, что проблемы, которые я описал, начинают проявляться, только когда вы достаточно продвинуты в работе с TDD ... Когда вы только начинаете работать с TDD (или работаете над небольшими проектами), рефакторинг тестов не будет проблемой.

Рышард Сзопа
источник
3
+1. «должен поддерживаться»: это гораздо меньшая проблема при тестировании повторно используемого кода, поскольку его интерфейс и поведение обычно должны быть стабильными. По этой причине я обычно делаю TDD только для нашей многоразовой библиотеки.
Дмитрий С.
4

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

Может быть, это только я. Но я также где-то читал, что автомобили со всевозможными защитными сигналами и трещинами имеют тенденцию к большему падению (потому что водители знают, что функции безопасности присутствуют), так что, возможно, это следует признать; TDD может быть несовместим с некоторыми людьми.

Joonas Pulakka
источник
Это кажется мне странным, поскольку написание тестируемого кода обычно заставляет меня замедляться и больше думать о том, что я кодирую. На самом деле я немного нервничаю, когда пишу без тестов.
Мэтт Х
1
Это просто показывает, что разные люди действительно реагируют по-разному. Я не ругаю TDD - очевидно, немало людей находят это полезным - но дело в том, что это не для всех.
Joonas Pulakka
2
Я согласен на 100%. Я пишу код лучше и быстрее без автоматизированных тестов. Конечно, было бы нелепо не проверять, я просто считаю, что автоматизация - плохой выбор (по крайней мере, для меня). Я считаю, что ручное тестирование и быстрее, чем обслуживание комплекта тестов, и безопаснее - но я также опытный разработчик, поэтому я очень хорошо знаю, что тестировать, где и почему, поэтому мои дополнения кода и повторные факторы регрессионного бесплатно.
Бен Ли
1
Хотя я должен отметить, что команда, с которой я работаю, и проекты достаточно малы, чтобы у меня было хорошее представление о всей архитектуре - в большой команде или в очень большом проекте я мог бы считать автоматизированные тесты более полезными, потому что тогда ни один разработчик не сможет понять, где они должны тестировать, чтобы избежать регрессии.
Бен Ли
Вы пропускаете этап рефакторинга?
Rjnilsson
2

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

Мой подход обычно:

  1. Реализуйте что-то, что работает (подтверждение концепции).
  2. Если это работает, консолидируйте, добавляя тесты, улучшая дизайн, рефакторинг.

Иногда я не попадаю на шаг 2.

В этом случае использование TDD оказалось для меня больше недостатков, чем преимуществ:

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

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

Джорджио
источник
1
Похоже, вы путаете код прототипа с пригодным для использования кодом. Код прототипа является тестовым кодом . Его не нужно тестировать, и вы не должны создавать тесты, которые работают против него. Шаг, который вы пропускаете, находится между 1. и 2 .: вы говорите: «консолидируйтесь, написав тесты». Проблема в том, что вам не нужно что-то консолидировать, но что-то писать. Планируйте переписать прототип кода, не планируйте его повторное использование. Повторное использование оставляет много места для компромисса. Переписывание формализует разделение между вашей фазой исследования и фазой «кода качества».
utnapistim
3
@utnapistim: Я не путаю код прототипа с пригодным для использования кодом, скорее, фанатики TDD путают их и предлагают вам также использовать TDD для кода прототипа. Вернее, они предполагают, что кода-прототипа вообще нет. Кроме того, я согласен с вами, что вам часто приходится переписывать, когда вы переходите от прототипа к реальной реализации. Иногда вы можете повторно использовать части кода прототипа, но вы должны быть готовы переписать. Вы действительно должны решить от случая к случаю.
Джорджио
3
@utnapistim: См. также ответ luis.espinal: «Самый большой недостаток, который я видел, связан не с самим TDD, а с практиками. Они придерживаются догматического и фанатичного подхода, когда все должно быть проверено».
Джорджио
1

Недостатки или стоимость TDD

Примечание. Существует ряд различных видов TDD. Независимо от единицы, BDD, ATDD или других вариантов многие трудности остаются

Побочные эффекты

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

  • Насмешка: забудь отстаивать порядок звонков
  • Насмешка: издевательство не соответствует реальному звонку или ответу
  • Fixture: тест опирается на нереальные данные, маскируя другие проблемы
  • Крепеж: проверить невозможное состояние в производстве
  • Функциональный: ложные обрывы сборки из-за временной недоступности зависимой системы
  • Функционально: скорость теста очень низкая

Вам придется изменить свой подход к кодированию, для некоторых это будет радикальное изменение.

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

Требуется время, чтобы понять, что вы заботитесь о тестировании и что вас не волнует о тестировании.

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

Конкретные юнит-тесты: большие рефакторы

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

dietbuddha
источник
0

Моя аналогия - барьеры на Scalextric. Если вы наденете их, вы будете намного менее осторожны.

Люди также получают немного пространства для своих тестов - потому что они работают нормально, они считают, что код полностью протестирован, тогда как это только начало процесса тестирования.

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

Робби Ди
источник
-1

Преимущества TDD в том, что он заставляет вас защищать свой код от людей, которые его не понимают. Да, это часто включает в себя. Но что происходит, когда код не стоит охранять? Существует много кода, которого вообще не должно быть! Таким образом, проблема с TDD заключается в том, что речь идет о разработчиках, которые пишут плохой код. TDD, вероятно, не поможет им написать хороший код, скорее всего, они также напишут ужасные тесты. Таким образом, в их случае TDD только добавит беспорядка; плохо написанные и / или избыточные тесты не более забавны, чем другие формы плохого кода.

Johan
источник
1
Если вы сами не понимаете свой собственный код, как может горстка из миллиардов возможных тестовых случаев защититься от неправильного кода?
Майкл Шоу
2
Потому что ты понял это, когда написал, но забыл об этом?
Йохан
+1 TDD не защищает от разработчика, который неправильно понял требования бизнеса. Вот где приходит BDD ...
Робби Ди