Как детка твои шаги в TDD?


37

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

Задача для ввода «1,2» - возвращаемая сумма чисел, равная 3. Что я написал (в C #):

numbers = input.Split(',');
return int.Parse(numbers[0]) + int.Parse(numbers[1]); //task said we have two numbers and input is correct

Но другие парни предпочитали делать это по-другому. Сначала для ввода «1,2» добавили следующий код:

if (input == "1,2")
   return 3;

Затем они ввели еще один тест для ввода «4,5» и изменили реализацию:

if (input == "1,2")
   return 3;
else if (input == "4,5")
   return 9;

И после этого они сказали: «Хорошо, теперь мы видим образец» и реализовали то, что я первоначально сделал.

Я думаю, что второй подход лучше соответствует определению TDD, но ... мы должны быть настолько строги в этом? Для меня это нормально пропустить тривиальные шаги ребенка и объединить их в «двойные шаги», если я уверен, что я ничего не пропущу. Я ошибся?

Обновить. Я сделал ошибку, не уточнив, что это был не первый тест. Уже было несколько тестов, поэтому «return 3» на самом деле был не самым простым фрагментом кода, удовлетворяющим требованию.


25
Так крошечная , что мои коллеги фонтанировать «Ooahhh dazso cuuuuuute»
Адель

6
@ Адель: Почти задохнулся от моего завтрака, клавиатура теперь переполнена или плевок и крошки
Binary Worrier

2
@ Адель, что касается не носителей языка, мне довольно сложно понять этот юмор, но я думаю, что вашим коллегам нравится вопрос :)
SiberianGuy

8
@Idsa: Это транспонирование ответа коллег, когда показаны первые шаги ребенка "Ooahhh dazso cuuuuuute" = "О, это так мило" (произнесено в голосе "песня не такая уж и милая"), с их ответом при просмотре модульных тестов, написанных Адель, при взгляде на шаги ребенка, они говорят: «О, это так мило». Реакция на реальные шаги ребенка = реакция на юнит-тесты "шаги ребенка".
бинарный беспорядок

3
@Binaryworrier хотел бы, чтобы я мог дать вам реальные очки за то, что
нашел

Ответы:


31

Напишите самый простой код, который делает тесты успешными.

Насколько я понимаю, никто из вас этого не сделал.

Малыш Шаг 1.

Тест: Для ввода «1,2» возвращается сумма чисел, равная 3

Сделать тест неудачным:

throw NotImplementedException();

Сделайте тестовый проход:

return 3;

Малыш Шаг 2.

Тест: Для ввода «1,2» возвращается сумма чисел, которая равна 3

Тест: Для ввода «4,5» возвращается сумма чисел, которая равна 9

Второй тест не пройден, поэтому сделайте его успешным:

numbers = input.Split(',');
return int.Parse(numbers[0]) + int.Parse(numbers[1]);

(Гораздо проще, чем список if ... return)

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

Аргумент заключается в том, что если вы не напишите второй тест, тогда может появиться какая-то яркая искра и «рефакторинг» вашего кода для чтения:

return input.Length; # Still satisfies the first test

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


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

@Idsa - Да, конечно, и чем больше тестов ты пишешь, тем более безумной должна быть реализация. input.Lengthне так уж и далеко, особенно если входные данные для метода оказываются измерением из какого-то файла и вы случайно назвали свой метод Size().
фунтовые

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

1
У меня вопрос по поводу самого "теста". Не могли бы вы написать новый тест для ввода «4,5» или изменить исходный тест?
mxmissile

1
@mxmissile: я бы написал новый тест. Это не займет много времени, и вы получите в два раза больше тестов, чтобы защитить себя, когда будете выполнять рефакторинг позже.
фунтовые

50

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

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


+1 и спасибо, что заставили меня посмотреть и выучить новое слово (асинин)
Марьян Венема

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

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

4
@pdr: я согласен с вами, что должно быть больше тестов для обработки крайних случаев. Когда вы пишете их и замечаете, что ваша реализация должна измениться, чтобы справиться с ними, сделайте это всеми средствами. Я думаю, у меня просто проблема с принятием шагов зиготы к первому «счастливому пути», «очевидной реализации», вместо того, чтобы просто записать это и перейти оттуда. Я не вижу смысла в том, чтобы выписывать утверждение if, которое знает каждое волокно в моем теле, просто исчезнет в следующий момент.
Кристоф Ванфлетерен

1
@ChristopheVanfleteren: Когда Бек описывает очевидную реализацию, он использует сумму двух целых в качестве примера и по- прежнему выдает огромное драматическое предупреждение о том, как вы умрете от стыда, если ваша пара / обозреватель может придумать более простой код, который делает тест проходят. Это абсолютная уверенность, если вы пишете только один тест для этого сценария. Кроме того, я могу придумать как минимум три "очевидных" способа решения этой проблемы: разделить и добавить, заменить запятую на + и оценить или использовать регулярное выражение. Задача TDD - помочь вам сделать правильный выбор.
фунтовые

19

Кент Бек рассказывает об этом в своей книге «Разработка через тестирование: на примере».

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

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

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

Из книги:

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


18

Я вижу это как следование букве закона, но не его духу.

Ваши шаги ребенка должны быть:

Как можно проще, но не проще.

Кроме того, глагол в методе sum

if (input == "1,2")
   return 3;

это не сумма, это тест для конкретных входных данных.


4

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

OTOH это относится только к действительно тривиальным шагам, как в примере выше. Для чего-то более сложного, что я не могу полностью запомнить в уме и / или когда я не уверен на 110% в результате, я предпочитаю делать один шаг за раз.


1

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

Теперь, когда я собираюсь написать какое-либо приложение, я обычно начинаю с теста. Какой шаг в этом тесте во многом связан с моим пониманием того, что я пытаюсь сделать. Если я думаю, что у меня в голове есть поведение класса, тогда шаг будет большим. Если проблема, которую я пытаюсь решить, гораздо менее ясна, то, возможно, просто скажем, что я знаю, что происходит переход к методу с именем X и что он вернет Y. В этот момент у метода даже не будет никаких параметров и есть вероятность, что имя метода и тип возвращаемого значения будут изменены. В обоих случаях тесты ведут мою разработку. Они рассказывают мне вещи о моем заявлении:

Этот класс, который у меня в голове, действительно будет работать?

или

Как, черт возьми, я вообще собираюсь делать это?

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

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

Я надеюсь, вам понравилось ваше обучение палке с TDD. ИМХО, если бы больше людей было заражено тестом, мир стал бы лучше :)


1

В учебнике о модульном тестировании я прочитал тот же подход (шаги, которые выглядят очень, очень маленькими) и как ответ на вопрос «какими маленькими они должны быть», что-то мне понравилось, что было (перефразировано) так:

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

Так что, может быть, ваш коллега немного застенчивый :)


1

Разве не весь смысл в том, что реализация метода не имеет значения, если тесты пройдут успешно? Расширение тестов завершится быстрее во втором примере, но в обоих случаях может быть неудачным.


1
Это не имеет значения, если вы совершенно не заботитесь о том, чтобы тратить время впустую
SiberianGuy

1

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

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


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