Як дитині ваші дитячі кроки в 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, але ... чи варто бути таким суворим? Для мене нормально пропустити тривіальні дитячі кроки та об'єднати їх у "двічі кроків", якщо я досить впевнений, що я нічого не пропущу. Я помиляюся?

Оновлення. Я помилився, не уточнивши, що це був не перший тест. Уже були деякі тести, тому "повернення 3" насправді не було найпростішим фрагментом коду для задоволення вимоги.


25
Настільки крихітні, що мої колеги гулять "Ооахх дазсо куууууте"
— Адель

6
@Adel: Майже задихнувся від мого сніданку, клавіатура зараз повна або колючка та крихти
— Binary Worrier

2
@Adel, що стосується того, хто не є носієм мови, мені досить складно зрозуміти цей гумор, але, мабуть, вашим колегам подобається питання :)
— SiberianGuy

8
@Idsa: Він переносить відповідь колег, коли показують дитині перші кроки "Ооааааааааа" = "О, це так мило" (вимовляється співаючою піснею-не-що-дуже-мило), з їхньою відповіддю коли бачите Unit Tests, написану Адель, дивлячись на дитячі кроки, вони говорять "О, це так мило". Реакція на - дійсні кроки для немовлят = реакція на одиничні тести "дитячі кроки".
— Бінарний занепокоєння

3
@Binaryworrier бажаю, щоб я міг дати вам реальні моменти, щоб витратити час на пояснення батьків
— Ендрю Т Фіннелл

Відповіді:


31

Напишіть найпростіший код, завдяки якому тести проходять.

Ніхто з вас цього не зробив, наскільки я бачу.

Baby Step 1.

Тест: Для введення "1,2" повернути суму чисел, яка дорівнює 3

Зробіть тест невдалим:

throw NotImplementedException();

Зробіть тестовий пропуск:

return 3;

Baby Step 2.

Тест: Для введення "1,2" повернути суму чисел, що дорівнює 3

Тест: Для введення "4,5" повернути суму чисел, що дорівнює 9

Другий тест не вдається, тому пройдіть його:

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

(Шлях простіше, ніж список, якщо ... повернути)

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

Аргумент полягає в тому, що якщо ви не напишете другий тест, то пізніше може з’явитися якась яскрава іскра, яка «переробляє» ваш код, щоб прочитати:

return input.Length; # Still satisfies the first test

І, не вживаючи обох кроків, ви ніколи не змушували другий тест ставати червоним (це означає, що сам тест є підозрюваним).


Що стосується Вашого вкладу. Приклад довгих, з таким же успіхом я можу уявити собі шалену неправильну реалізацію, яка не буде спіймана обома тестами
— SiberianGuy

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

6
+1. Що стосується того, як вивчити TDD, це правильний шлях. Після того, як ви дізнаєтесь про це, іноді ви можете перейти безпосередньо до очевидного впровадження, але щоб відчути TDD, це набагато краще.
— Карл Манастер

1
У мене питання стосовно самого "тесту". Ви б написали новий тест для введення "4,5" чи змінили оригінальний тест?
— mxmissle

1
@mxmissle: Я б написав новий тест. Це не займе багато часу, і ви закінчите вдвічі більше тестів, щоб захистити вас, коли пізніше ви зробите рефакторинг.
— пдр

50

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

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


+1 і спасибі за те, що змусив мене шукати і вивчати нове слово (асинін)
— Мар'ян Венема

12
+1 за те, що називав це нерозумно дурним. TDD - це все приємно і таке, але, як і будь-яка сучасна техніка розкрученого програмування, ви повинні подбати про те, щоб не загубитися.
— вівторок

2
"Особливо, якщо оригінальна проблема, яку ви вирішуєте, вже дуже мала". - Якби введення було два входи, які потрібно додати разом, я би погодився з цим, але я не переконаний, коли це "розділити рядок, проаналізувати два ints з результату та додати їх". Більшість методів у реальному світі не набагато складніші за це. Насправді має бути ще тестів, щоб охопити крайні випадки, такі як знаходження двох коми, нецілих значень тощо
— pdr

4
@pdr: Я згоден з вами, що для обробки кращих справ має бути більше тестів. Коли ви пишете їх і помічаєте, що ваша реалізація повинна змінитись, щоб обробити їх, усіляко зробіть це. Напевно, у мене просто виникає проблема з тим, як зробити кроки зиготи до першого щасливого шляху, "очевидного впровадження", замість того, щоб просто записати це і піти звідти. Я не бачу значення в написанні заяви if, про яке знає кожна клітковина в моєму тілі, просто зникне наступного моменту.
— Крістоф Ванфлетерен

1
@ChristopheVanfleteren: Коли Бек описує очевидну реалізацію, він використовує суму двох точок як приклад і все-таки кидає величезне драматичне попередження про те, як ти помреш від сорому, якщо твоя пара / рецензент може придумати простіший код, який робить пропускний тест. Це абсолютна впевненість, якщо ви пишете лише один тест для цього сценарію. Крім того, я можу придумати щонайменше три "очевидні" способи вирішення цієї проблеми: розділити та додати, замінити кому на + та оцінити, або використовувати регулярний вираз. Сенс TDD полягає в тому, щоб привести вас до правильного вибору.
— pdr

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.