Сегодня мы тренировали 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» на самом деле был не самым простым фрагментом кода, удовлетворяющим требованию.
Ответы:
Напишите самый простой код, который делает тесты успешными.
Насколько я понимаю, никто из вас этого не сделал.
Малыш Шаг 1.
Тест: Для ввода «1,2» возвращается сумма чисел, равная 3
Сделать тест неудачным:
Сделайте тестовый проход:
Малыш Шаг 2.
Тест: Для ввода «1,2» возвращается сумма чисел, которая равна 3
Тест: Для ввода «4,5» возвращается сумма чисел, которая равна 9
Второй тест не пройден, поэтому сделайте его успешным:
(Гораздо проще, чем список if ... return)
В этом случае вы, конечно, можете утверждать о Очевидной реализации, но если вы говорили о том, чтобы делать это строго по шагам ребенка, то это правильные шаги, ИМО.
Аргумент заключается в том, что если вы не напишите второй тест, тогда может появиться какая-то яркая искра и «рефакторинг» вашего кода для чтения:
И, не предпринимая обоих шагов, вы никогда не сделали второй тест красным (это означает, что сам тест является подозрительным).
источник
input.Length
не так уж и далеко, особенно если входные данные для метода оказываются измерением из какого-то файла и вы случайно назвали свой методSize()
.Я думаю, что второй способ - глупый ум. Я вижу ценность в том, чтобы делать достаточно маленькие шаги, но написание этих крошечных зигот (не могу даже назвать их детками) шагов - просто глупость и пустая трата времени. Особенно, если исходная проблема, которую вы решаете, уже очень мала.
Я знаю, что это тренировка, и это больше о демонстрации принципа, но я думаю, что такие примеры делают TDD скорее плохим, чем хорошим. Если вы хотите показать значение шагов ребенка, по крайней мере, используйте проблему, в которой есть какая-то ценность.
источник
Кент Бек рассказывает об этом в своей книге «Разработка через тестирование: на примере».
Ваш пример указывает на « очевидную реализацию » - вы хотите вернуть сумму двух входных значений, и это довольно простой алгоритм для достижения. Ваш контрпример упал в «подделка, пока вы не сделаете это» (хотя это очень простой случай).
Очевидная реализация может быть намного более сложной, чем эта, но в основном она срабатывает, когда спецификация метода довольно жесткая - например, возвращать версию свойства класса в кодировке URL - вам не нужно тратить время на кучу поддельные кодировки
С другой стороны, подпрограмме соединения с базой данных потребуется немного больше обдумать и протестировать, поэтому очевидной реализации не будет (даже если вы уже писали ее несколько раз в других проектах).
Из книги:
источник
Я вижу это как следование букве закона, но не его духу.
Ваши шаги ребенка должны быть:
Кроме того, глагол в методе
sum
это не сумма, это тест для конкретных входных данных.
источник
Мне кажется, что хорошо объединить несколько тривиальных этапов реализации в один чуть менее тривиальный - я тоже так делаю постоянно. Я не думаю, что нужно быть религиозным о следовании TDD каждый раз к письму.
OTOH это относится только к действительно тривиальным шагам, как в примере выше. Для чего-то более сложного, что я не могу полностью запомнить в уме и / или когда я не уверен на 110% в результате, я предпочитаю делать один шаг за раз.
источник
Как только этот вопрос проиллюстрирован, когда вы начинаете с TDD, размер шагов может стать проблемой. Вопрос, который я часто задавал себе, когда начинал писать тестовые приложения; Помогает ли тест, который я пишу, разрабатывать мои приложения? Некоторым это может показаться тривиальным и не связанным, но на мгновение задержитесь там со мной.
Теперь, когда я собираюсь написать какое-либо приложение, я обычно начинаю с теста. Какой шаг в этом тесте во многом связан с моим пониманием того, что я пытаюсь сделать. Если я думаю, что у меня в голове есть поведение класса, тогда шаг будет большим. Если проблема, которую я пытаюсь решить, гораздо менее ясна, то, возможно, просто скажем, что я знаю, что происходит переход к методу с именем X и что он вернет Y. В этот момент у метода даже не будет никаких параметров и есть вероятность, что имя метода и тип возвращаемого значения будут изменены. В обоих случаях тесты ведут мою разработку. Они рассказывают мне вещи о моем заявлении:
Этот класс, который у меня в голове, действительно будет работать?
или
Как, черт возьми, я вообще собираюсь делать это?
Дело в том, что я могу переключаться между большими шагами и маленькими шагами в мгновение ока. Например, если большой шаг не работает, и я не вижу очевидного пути, я переключусь на меньший шаг. Если это не сработает, я переключусь на еще меньший шаг. Тогда есть другие методы, такие как триангуляция, если я действительно застрял.
Если вы, как и я, являетесь разработчиком, а не тестером, то смысл использования TDD состоит не в написании тестов, а в написании кода. Не зацикливайтесь на написании множества небольших тестов, если они не дают вам никаких преимуществ.
Я надеюсь, вам понравилось ваше обучение палке с TDD. ИМХО, если бы больше людей было заражено тестом, мир стал бы лучше :)
источник
В учебнике о модульном тестировании я прочитал тот же подход (шаги, которые выглядят очень, очень маленькими) и как ответ на вопрос «какими маленькими они должны быть», что-то мне понравилось, что было (перефразировано) так:
Речь идет о том, насколько вы уверены, что шаги работают. Вы можете сделать действительно большие шаги, если хотите. Но, попробуйте это в течение некоторого времени, и вы найдете много ошибочной уверенности в местах, где вы принимаете это как должное. Итак, тесты помогут вам построить уверенность на основе фактов.
Так что, может быть, ваш коллега немного застенчивый :)
источник
Разве не весь смысл в том, что реализация метода не имеет значения, если тесты пройдут успешно? Расширение тестов завершится быстрее во втором примере, но в обоих случаях может быть неудачным.
источник
Я согласен с людьми, которые говорят, что ни одна из них не является самой простой реализацией.
Причина, по которой методология настолько строга, заключается в том, что она обязывает вас написать как можно больше соответствующих тестов. Возвращать постоянное значение для одного тестового примера и называть его проходом - это хорошо, потому что это заставляет вас вернуться назад и указать, что вы действительно хотите, чтобы получить что-то кроме бессмыслицы из вашей программы. Использование такого тривиального случая в некоторых отношениях является ударом по ноге, но принцип заключается в том, что ошибки пробиваются в пробелах в вашей спецификации, когда вы пытаетесь сделать «слишком много», а снижение требования к простейшей возможной реализации гарантирует, что Тест должен быть написан для каждого уникального аспекта поведения, который вы на самом деле хотите.
источник