Почему бы не написать все тесты сразу при выполнении TDD?


55

Цикл Красный - Зеленый - Рефактор для TDD хорошо установлен и принят. Мы пишем один провальный модульный тест и делаем его максимально простым. Каковы преимущества этого подхода по сравнению с написанием множества неудачных модульных тестов для класса и заставляют их пройти все сразу?

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


20
Делайте то, что работает лучше для вас (после некоторых экспериментов). Слепое следование догме никогда не бывает хорошим.
Майкл Боргвардт

6
Полагаю, что написание всех ваших тестов одновременно очень похоже на написание всего кода вашего приложения одновременно.
Майкл Харен

1
@MichaelHaren Все тесты для класса (или функционального модуля), извините за путаницу
RichK

3
Решение проблемы «мозгового сброса»: иногда в тестировании / кодировании возникают моменты, когда вы понимаете необходимость нескольких различных входных тестов, и есть тенденция использовать выгоду из ясности этой реализации, прежде чем отвлекаться на мелочи кодирования. Я обычно управляю этим, поддерживая отдельный список (например, Mylyn), или же список комментариев в классе Test различных вещей, которые я хочу запомнить для тестирования (например, // test null case). Тем не менее, я все еще пишу только один тест за раз, и вместо этого систематически работаю в списке.
Сэм Голдберг

1
ну, я не знаю, почему никто не упомянул об этом, но вы НЕ МОЖЕТЕ написать все тесты сразу. Написание всех тестов перед отбором точно так же, как и выполнение BDUF . А чему нас научила история о BDUF? Это почти никогда не работает.
Сонго

Ответы:


50

Проектирование, основанное на тестировании, направлено на то, чтобы получить правильный API , а не код.

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

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


Отличные очки! Иногда мы настолько погружаемся в тестирование кода, что иногда игнорируем, насколько важными могут быть API и модель предметной области, прежде чем даже писать ваш первый тест.
maple_shaft

+1 за реальный подход к разработке, управляемой тестами .
Джошуа Дрейк

76

Когда вы пишете один тест, вы концентрируетесь на одном .
С помощью множества тестов вы распространяете свое внимание на множество задач, так что это не очень хорошая идея.


8
Кто бы это понизил ?!
CaffGeek

6
@ Если бы я не был против, но я считаю, что этот ответ упускает очевидное. Разработка через тестирование подразумевает использование тестов для разработки кода. Вы пишете тест индивидуально, чтобы развить дизайн, а не только для тестируемости. Если бы речь шла только об испытательных артефактах, то это был бы хороший ответ, но при этом отсутствует какая-то важная информация.
Джошуа Дрейк

7
Я не понизил это, но; Я думал об этом. Это слишком короткий ответ на сложный вопрос.
Марк Уэстон,

2
+1 за концентрацию на одной вещи за раз, наша способность к многозадачности переоценивается.
cctan

Это самый простой ответ, который может сработать.
ДНК

27

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

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

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


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

17

Я думаю, что, делая это, вы упускаете процесс TDD. Просто написав все свои тесты в начале, вы на самом деле не проходите процесс разработки с использованием TDD. Вы просто заранее угадываете, какие тесты вам понадобятся. Это будет совсем другой набор тестов, чем те, которые вы в конечном итоге будете писать, если будете выполнять их по одному во время разработки своего кода. (Если ваша программа не будет тривиальной по своей природе.)


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

5
@maple_shaft - технология может быть тривиальной, но бизнес-правила - нет. Попробуйте создать приложение для 5 менеджеров, которые предъявляют различные требования и отказываются прислушиваться к каким-либо советам о вашем простом, элегантном дизайне с минимальными размерами.
JeffO

5
@JeffO 1) Это не BS. 2) Элегантный минималистский дизайн требует хороших навыков разработки программного обеспечения. 3) Для того, чтобы смягчить требования от 5 разных менеджеров, у которых не более 5 минут в неделю, чтобы тратить с вами время и при этом выполнять минималистичный дизайн, требуется отличный разработчик программного обеспечения. Советы профессионалов: разработка программного обеспечения - это больше, чем просто навыки кодирования, это переговоры, беседа и принятие ответственности. Ты должен быть Альфа-собакой и кусать иногда.
maple_shaft

1
Если я правильно понимаю, этот ответ напрашивается на вопрос.
Конрад Рудольф

1
@maple_shaft Я думаю, что это было то, что Джефф О со своим комментарием, не так ли?
ZweiBlumen

10

Я «записываю» все тесты, которые могу придумать, во время «мозгового штурма», однако пишу каждый тест как отдельный комментарий, описывающий тест.

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

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

Теперь, если вы используете систему, подобную потоку спецификаций, когда тесты написаны на «английском», вы можете попросить клиентов согласиться на набор тестов, пока у вас есть время, а не только на создание одного теста.


1
Да, согласившись с ответами выше, которые указывают на проблемы с кодированием всех ваших тестов в первую очередь, я нахожу очень полезным выкинуть мое общее понимание того, как текущий метод должен вести себя как набор описаний тестов без какого-либо кода. Процесс их записи имеет тенденцию выяснять, полностью ли я понимаю, что требуется от кода, который я собираюсь написать, и есть ли крайние случаи, о которых я не задумывался. Мне гораздо удобнее кодировать первый тест, а затем выполнять его после того, как я изложил свой «обзор» того, как метод должен работать.
Марк Уэстон

10

Идея TDD - быстрые итерации.

Если у вас есть множество тестов, которые нужно написать перед написанием кода, сложно итеративно реорганизовать ваш код.

Без простого рефакторинга кода вы потеряете много преимуществ TDD.


5

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

Если вам нужна мозговая информация, у вас есть много вариантов:

  • Интерактивная доска
  • Пользовательские истории
  • Комментарии
  • Хорошая старая ручка и бумага

Обратите внимание, что нигде в этом списке нет компилятора. :-)


5

Вы предполагаете, что знаете, как будет выглядеть ваш код, прежде чем писать его. TDD / BDD - это такой же процесс разработки / обнаружения, как и процесс обеспечения качества. Для данной функции вы пишете простейший тест, который проверяет, удовлетворена ли функция (иногда это может потребовать несколько из-за сложности функции). Этот первый тест, который вы пишете, загружен предположениями о том, как будет выглядеть рабочий код. Если вы пишете весь набор тестов до того, как напишите первую строку кода для его поддержки, вы сделаете целый ряд непроверенных предположений. Вместо этого напишите одно предположение и проверьте его. Тогда напиши следующее. В процессе проверки следующего предположения вы можете просто нарушить более раннее предположение, поэтому вам придется вернуться назад или изменить это первое предположение, чтобы оно соответствовало реальности, или изменить реальность так, чтобы первое предположение все еще действовало.

Думайте о каждом модульном тесте, который вы пишете, как о теории в научной тетради. Заполняя тетрадь, вы доказываете свои теории и формируете новые. Иногда доказательство новой теории опровергает предыдущую теорию, поэтому вы должны ее исправить. Проще доказать одну теорию за раз, чем пытаться доказать, скажем, 20 сразу.


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

4

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

Ваша идея - это в основном подход Big Test Up Front, с которым ИМХО сложнее справиться, и он может стать более расточительным. Что если в середине вашей работы вы поймете, что ваш подход не очень хорош, ваш API имеет недостатки и вам нужно начинать все сначала или использовать стороннюю библиотеку вместо этого? Тогда большая часть работы по написанию ваших тестов будет потрачена впустую.

Тем не менее, если это работает для вас, хорошо. Я могу себе представить, что если вы работаете с фиксированной, подробной технической спецификацией, в домене, с которым у вас есть глубокий опыт, и / или над довольно небольшой задачей, у вас может быть готово большинство или все необходимые тестовые сценарии, и ваша реализация будет ясна прямо из начало. Тогда имеет смысл начать с написания всех тестов одновременно. Если ваш опыт показывает, что это делает вас более продуктивным в долгосрочной перспективе, вам не нужно слишком беспокоиться о книгах правил :-)


4

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

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

Понять, что должен делать метод, в виде комментариев, историй, функциональной спецификации и т. Д., Вполне приемлемо. Я бы подождал, чтобы перевести их в тесты по одному, хотя.

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


3

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

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


2
  • Затем вы пытаетесь сосредоточиться на слишком большом количестве вещей одновременно.
  • При реализации, чтобы все тесты прошли успешно, у вас нет рабочей версии вашего приложения. Если вам придется много реализовывать, у вас не будет рабочей версии в течение длительного времени.

2

Цикл Red-Green-Refactor - это контрольный список, предназначенный для начинающих разработчиков TDD. Я бы сказал, что было бы неплохо следовать этому контрольному списку, пока вы не узнаете, когда его выполнять, и когда вы можете его сломать (то есть, пока не знаете, не нужно задавать этот вопрос по stackoverflow :)

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


1

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

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

Обычно эти тесты указываются и выполняются в среде BDD, такой как Cucumber, Fitnesse или что-то подобное.

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

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


1

Один тест за раз: главное преимущество - сосредоточиться на одном. Подумайте о глубоком дизайне: вы можете углубиться и сосредоточиться на быстрой обратной связи. Вы можете пропустить суть всей проблемы, хотя! Это момент (большой) рефакторинг вступает в игру. Без этого TDD не работает.

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

Мне трудно вообще рекомендовать отдавать предпочтение одному или другому, потому что факторов много: опыт (особенно с той же проблемой), знание предметной области и навыки, удобство кода для рефакторинга, сложность проблемы ...

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


1

Предполагая, что ваш тестовый фреймворк поддерживает это, я хотел бы предложить вместо того, чтобы реализовывать тесты, которые вы хотите осуществить с помощью мозгового дампинга, вместо этого писать описательные ожидающие тесты, которые вы позже внедрите. Например, если ваш API должен выполнять foo и bar, но не biz, просто добавьте следующий код (этот пример на rspec) для вашего набора тестов, а затем атакуйте их один за другим. Вы быстро обдумываете свои мысли и можете решать все свои проблемы один за другим. Когда все тесты пройдут, вы узнаете, когда решите все проблемы, возникшие у вас во время мозгового приступа.

describe "Your API" do

  it "should foo" do
    pending "braindump from 4/2"
  end

  it "should bar" do
    pending "braindump from 4/2"
  end

  it "should not biz" do
    pending "braindump from 4/2"
  end

end
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.