Почуття одиничних тестів без TDD


28

У нас починається новий (досить великий) проект, який ми планували розробляти за допомогою TDD.

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

Додано: Я думаю, що це не дублікат >> цього питання << - я розумію різницю між UT та TDD. Моє запитання не в розбіжностях , а в сенсі написання модульних тестів без TDD.


22
Мені цікаво, що міркує ваш друг про таку абсурдну позицію ...
Теластин

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

2
Якими будуть ваші рівні інтеграції? Якими будуть ваші підрозділи? Як часто ви будете рефакторинг нижче кожного рівня тесту? Наскільки швидко пройдуть ваші інтеграційні тести? Як легко їм буде писати? Скільки комбінаторних випадків генерують різні частини вашого коду? і т.д. ... Якщо ви не знаєте відповідей на них, то, можливо, ще рано приймати тверді рішення. Іноді TDD - це чудово. Іноді переваги менш зрозумілі. Іноді одиничні тести є важливими. Іноді гідний набір інтеграційних тестів купує вам майже стільки ж, і набагато гнучкіші ... Тримайте параметри відкритими.
Топо Відновити Моніку

2
Як деякі практичні поради з досвіду, не перевіряйте все, якщо ви не робите TDD. Визначте, які види тестів є цінними. Я виявив, що одиничні тести на чистих методах введення / виведення надзвичайно цінні, тоді як інтеграційні тести на дуже високому рівні програми (наприклад, фактично надсилання веб-запитів у веб-додаток) також надзвичайно цінні. Слідкуйте за тестами інтеграції середнього рівня та тестовими підрозділами, які потребують великої кількості макетів. Також дивіться це відео: youtube.com/watch?v=R9FOchgTtLM .
jpmc26

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

Відповіді:


52

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

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


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

5
@DougM - Можливо, в ідеальному світі ...
Теластин

7
На жаль TDD йде рука об руку з глузуванням, і поки люди не перестануть робити це, все це доводить - це тест працює швидше . TDD мертвий. Тестування на довге живе
MickyD

17
TDD не гарантує покриття коду. Це небезпечне припущення. Ви можете кодувати проти тестів, здавати ці тести, але все-таки мати крайні регістри.
Роберт Харві

4
@MickyDuncan Я не зовсім впевнений, що цілком розумію вашу стурбованість. Знущання - це абсолютно дійсна методика, яка використовується для ізоляції одного компонента від інших, щоб тести поведінки цього компонента можна було виконати незалежно. Так, у крайній мірі це може призвести до надмірного проектування програмного забезпечення, але так само може застосовуватися будь-яка техніка розробки при неправильному використанні. Крім того, як зазначає DHH у статті, яку ви цитуєте, ідея використовувати колись повні системні тести так само погана, якщо не зовсім гірша. Важливо використовувати судження, щоб вирішити, який найкращий спосіб тестування будь-якої конкретної особливості .
Жуль

21

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

У будь-якому разі , я нещодавно використовував сторонні бібліотеки для виконання конкретного завдання. Виникла проблема щодо використання цієї бібліотеки, тож я поклав на себе, щоб по суті писати версію цієї самої бібліотеки. Загалом це було приблизно 5000 рядків виконуваного коду і приблизно 2 місяці мого часу. Я знаю, що рядки коду - це поганий показник, але я вважаю, що це гідний показник масштабу.

Була потрібна одна конкретна структура даних, яка дозволила б мені відстежувати довільну кількість біт. Оскільки проект знаходиться в Java, я вибрав Java BitSetі трохи змінив її (мені потрібна була можливість відстежувати і провідні 0s, що чомусь не робить BitSet Java .....). Досягнувши ~ 93% покриття, я почав писати кілька тестів, які б насправді наголосили на системі, яку я написав. Мені потрібно було порівняти певні аспекти функціональності, щоб гарантувати, що вони будуть досить швидкими для моїх кінцевих вимог. Не дивно, що одна з функцій, яку я перекрив з BitSetінтерфейсу, була абсурдно повільною при роботі з великими наборами бітів (в цьому випадку сотні мільйонів біт). Інші перекриті функції покладалися на цю одну функцію, тому це була величезна шийка пляшки.

Те, що я в кінцевому підсумку робив, - це переходити до креслярської дошки і з'ясовувати спосіб маніпулювання базовою структурою BitSet, яка є long[]. Я розробив алгоритм, попросив колег про їх введення, а потім почав писати код. Потім я провів одиничні тести. Деякі з них зламалися, а ті, що зробили, вказали мені саме туди, куди мені потрібно заглянути в своєму алгоритмі, щоб виправити це. Виправивши всі помилки в одиничних тестах, я зміг сказати, що функція працює як слід. Принаймні, я можу бути таким же впевненим, що цей новий алгоритм спрацював так само, як і попередній алгоритм.

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

Це сенс проводити одиничні тести незалежно від TDD - тобто модульні тести зроблять це для вас в TDD і поза TDD все одно, коли ви закінчите рефакторинг / підтримку коду. Звичайно, це слід поєднувати з регулярними тестами регресії, тестуванням диму, нечітким тестуванням і т. Д., Але одиничне тестування, як зазначено в назві, тестує речі на найменшому, атомному рівні, що дає вам змогу вказувати, де виникли помилки.

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


7

Ви можете розбити код приблизно на 4 категорії:

  1. Просте і рідко змінюється.
  2. Прості та часто змінюються.
  3. Складна і рідко змінюється.
  4. Складні і часто змінюються.

Експертні тести стають більш цінними (швидше за все, підхоплюють важливі помилки), чим далі ви потрапляєте у список. У своїх особистих проектах я майже завжди роблю TDD категорії 4. У категорії 3 я зазвичай роблю TDD, якщо ручне тестування не простіше і швидше. Наприклад, антиалізійний код було б складно написати, але набагато простіше перевірити візуально, ніж писати одиничний тест, тому тест одиниці міг би мені цього вартий, якщо цей код часто змінювався. Решту мого коду я виставляю під тест одиниці лише після виявлення помилки в цій функції.

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


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

1

Нехай це будуть одиничні, компонентні, інтеграційні або приймальні тести, важливою частиною є те, що вони повинні бути автоматизовані. Відсутність автоматизованих тестів є фатальною помилкою для будь-якого типу програмного забезпечення - від простих CRUD до найскладніших розрахунків. Аргументація полягає в тому, що написання автоматизованих тестів завжди буде коштувати менше, ніж безперервна потреба в запуску всіх тестів вручну, коли ви цього не зробите, на порядки. Після того, як ви їх записали, вам просто потрібно натиснути кнопку, щоб побачити, чи проходять вони чи не виходять з ладу. Ручні тести завжди будуть тривати довгий час, і залежати від людини (живих істот, які нудьгують, можуть бракувати уваги тощо), щоб мати можливість перевірити, чи пройдуть тести чи не вдасться. Словом, завжди пишіть автоматизовані тести.

Тепер, про причину, через яку ваш колега може проти того, щоб проводити будь-яке тестування одиниць без TDD: Це, мабуть, тому, що важче довіряти тестам, написаним після виробничого коду. І якщо ви не можете довіряти своїм автоматизованим тестам, вони нічого не варті . Після циклу TDD спочатку потрібно зробити тест з ладу (з правильної причини), щоб йому було дозволено записати виробничий код, щоб він пройшов (і не більше). Цей процес по суті перевіряє ваші тести, тому ви можете їм довіряти. Не кажучи вже про те, що написання тестів перед фактичним кодом підштовхує вас до спроектування ваших підрозділів та компонентів, які будуть легше перевірятими (високий рівень розв’язки, застосовано SRP тощо). Хоча, звичайно, робити TDD вимагає дисципліни .

Натомість, якщо ви спочатку пишете весь виробничий код, коли ви пишете тести для нього, ви очікуєте, що вони пройдуть під час першого запуску. Це дуже проблематично, тому що ви, можливо, створили тест, який охоплює 100% вашого виробничого коду, не стверджуючи правильну поведінку (можливо, навіть не виконувати жодних тверджень! Я бачив, що це трапляється ), оскільки ви не можете бачити, що він не працює спочатку перевірити, чи не вдається вона з правильної причини. Таким чином, у вас можуть бути помилкові позитиви. Помилково позитивні зрештою порушать довіру до вашого тестового набору, по суті змушуючи людей знову вдаватися до ручного тестування, тому у вас є вартість обох процесів (написання тестів + ​​ручні тести).

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

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


0

Часто якість тестів залежить від їх походження. Я регулярно винен у тому, що не роблю "справжнього" TDD - я пишу якийсь код, щоб довести, що стратегія, яку я хотів би використовувати, насправді працює, а потім висвітлюю кожен випадок, який код повинен підтримувати тестами. Зазвичай одиниця коду є класом, щоб дати вам загальне уявлення про те, скільки роботи я із задоволенням виконую без тестового покриття - тобто, не великої кількості. Це означає, що семантичне значення тестів добре узгоджується із тестованою системою у її «закінченому» стані - тому що я написав їх, знаючи, які випадки виконує SUT та як їх виконує.

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


0

Насправді дядько Боб згадав дуже цікавий момент в одному зі своїх відео про чисті кодери. Він сказав, що цикл Червоно-Зелений-Рефактор можна застосувати двома способами.

1-й - звичайний спосіб TDD. Напишіть невдалий тест, потім зробіть тестовий пропуск і, нарешті, рефактор.

Другий спосіб - написати дуже маленький шматочок виробничого коду і негайно слідувати за його тестовою одиницею, а потім рефактором.

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

Ще раз повторюю (і на цьому наголосив дядько Боб) ідея полягає в тому, щоб зробити дуже маленькими кроками і негайно випробувати те, що було додано.


"... ідея полягає в тому, щоб піти дуже маленькими кроками і негайно перевірити фрагмент виробничого коду, який щойно був доданий.": Я не згоден. Що ви описуєте, якщо добре, коли у вас вже є чітке уявлення про те, що ви хочете зробити, і ви хочете попрацювати над деталями, але вам потрібно спершу отримати велику картину. В іншому випадку, пройшовши дуже маленькими кроками (тестувати, розробляти, тестувати, розвивати), ви можете загубитися в деталях. "Якщо ви не знаєте, куди їдете, можливо, туди не потрапите".
Джорджіо
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.