Чи нормально витрачати стільки, якщо не більше, часу на тести, ніж фактичний код?


211

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

Це нормально чи я роблю щось не так?

Питання « Чи варто тестування одиниць або тестова розробка? "," Ми витрачаємо більше часу на тестування функціоналу, ніж на реалізацію самої системи, це нормально? "Та їх відповіді стосуються того, чи варто тестування (як у" чи варто взагалі пропустити тести з написання? "). Хоча я переконаний, що тести важливі, мені цікаво, чи нормально витрачаю більше часу на тести, ніж фактичний код, чи це лише я.

Судячи з кількості переглядів, відповідей і підтверджує моє отримане запитання, я можу лише припустити його законну стурбованість, яку не вирішено жодним іншим питанням на веб-сайті.


20
Анекдотично, але я вважаю, що витрачаю приблизно стільки ж часу на тестування письма, скільки на написання коду, коли я TDD. Це коли я ковзаю і пишу тести після того, як я витрачаю більше часу на тести, ніж на код.
RubberDuck

10
Ви також витрачаєте більше часу на читання, ніж на написання коду.
Thorbjørn Ravn Andersen

27
Також тести є фактичним кодом. Ви просто не доставляєте цю частину клієнтам.
Thorbjørn Ravn Andersen

5
В ідеалі ви витратите більше часу на біг, ніж на написання коду. (Інакше ви просто зробите завдання вручну.)
Джошуа Тейлор

5
@RubberDuck: Тут протилежний досвід. Іноді, коли я пишу тести після факту, код і дизайн вже досить охайні, тому мені не потрібно занадто переписувати код і тести. Тому для написання тестів потрібно менше часу. Це не правило, але це трапляється зі мною досить часто.
Джорджіо

Відповіді:


205

Я пам’ятаю з курсу інженерії програмного забезпечення, що один витрачає ~ 10% часу на розробку на написання нового коду, а інший 90% на налагодження, тестування та документацію.

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

Нарешті тести також повинні подвоїтися як документація! Слід записати одиничні тести так, як призначено використовувати код; тобто тести (і використання) повинні бути простими, вкладати складні речі в реалізацію.

Якщо ваші тести важко написати, то код, який вони перевіряють, мабуть, важко використовувати!


4
Можливо, вдалий час, щоб розібратися, чому код так важко перевірити :) Спробуйте "розкрутити" складні багатофункціональні лінії, які не є строго необхідними, як масово вкладені бінарні / потрійні оператори ... Я дуже ненавиджу непотрібні бінарні / потрійний оператор, який також має бінарний / потрійний оператор як один із шляхів ...
Нельсон,

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

Я вже говорив про це в іншому місці, але тестування одиниць, як правило, працює набагато довше, тому що більшість кодів має тенденцію дотримуватися свого роду "Принцип Парето": ви можете охопити приблизно 80% вашої логіки з приблизно 20% коду, який потрібно покрийте 100% вашої логіки (тобто покриття всіх крайових справ займає приблизно в п’ять разів більше коду тестування одиниць). Звичайно, залежно від основи, ви можете ініціалізувати середовище для декількох тестів, зменшуючи загальний код, необхідний, але навіть це вимагає додаткового планування. Наближення до 100% впевненості вимагає набагато більше часу, ніж просто тестування основних шляхів.
фірфокс

2
@phyrfox Я думаю, що це занадто обережно, це більше схоже на "інші 99% коду - це кращі регістри". Це означає, що інші 99% тестів призначені для тих крайніх випадків.
Móż

@Nelson Я погоджуюсь, що вкладені потрійні оператори важко читати, але я не думаю, що вони роблять тестування особливо складним (хороший інструмент покриття підкаже, якщо ви пропустили одну з можливих комбінацій). IMO, програмне забезпечення важко перевірити, коли воно занадто щільно з'єднане або залежить від провідних даних або даних, які не передаються як параметр (наприклад, коли умова залежить від поточного часу, і це не передається як параметр). Це не пов'язано безпосередньо з тим, наскільки "читабельним" є код, хоча, звичайно, всі інші речі рівні, читабельний код краще!
Андрес Ф.

96

Це є.

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

Розглянемо простий код:

public void SayHello(string personName)
{
    if (personName == null) throw new NullArgumentException("personName");

    Console.WriteLine("Hello, {0}!", personName);
}

Якими були б тести? Тут можна перевірити як мінімум чотири прості випадки:

  1. Ім'я людини - це null. Виключення насправді кинуто? Ось як мінімум три рядки тестового коду для запису.

  2. Ім'я людини - це "Jeff". Чи отримуємо ми "Hello, Jeff!"у відповідь? Це чотири рядки тестового коду.

  3. Ім'я людини - це порожня рядок. Якого результату ми очікуємо? Який фактичний вихід? Побічне запитання: чи відповідає функціональним вимогам? Це означає ще чотири рядки коду для одиничного тесту.

  4. Ім’я людини досить коротке для рядка, але занадто довге, щоб поєднуватися із "Hello, "знаком оклику. Що відбувається? ¹

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

Якщо коефіцієнт дуже великий, то в такому випадку ви можете перевірити кілька речей:

  • Чи є дублювання коду в тестах? Той факт, що це тестовий код, не означає, що код слід дублювати (копіювати) між аналогічними тестами: таке дублювання ускладнить підтримку цих тестів.

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

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

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

Примітка про TDD

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


¹ Нехай п буде максимальна довжина рядка . Ми можемо зателефонувати SayHelloта передати посиланням рядок довжиною n - 1, який повинен працювати добре. Тепер на Console.WriteLineетапі форматування має закінчитися рядком довжиною n + 8, що призведе до винятку. Можливо, через обмеження пам'яті навіть рядок, що містить n / 2 символів, призведе до винятку. Питання, яке слід задати, полягає в тому, чи є цей четвертий тест одиничним тестом (він схожий на один, але може мати набагато більший вплив щодо ресурсів порівняно із середніми одиничними тестами) та чи перевіряє він фактичний код або базовий фреймворк.


5
Не забувайте, що у людини може бути і ім’я недійсне. stackoverflow.com/questions/4456438 / ...
psatek

1
@JacobRaihle Я припускаю, що @MainMa означає значення fits personNameв a string, але значення personNameплюс об'єднані значення переливаються string.
woz

@JacobRaihle: Я відредагував свою відповідь, щоб пояснити це. Дивіться виноску.
Арсеній Муренко

4
As a rule of thumb, if you remove a unit test, the branch coverage should decrease.Якщо я напишу всі чотири вищевказані тести, а потім видалю 3-й тест, чи зменшиться покриття?
Вівек

3
"досить довго" → "занадто довго" (у пункті 4)?
Paŭlo Ebermann

59

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

Хоча одиничне тестування є необхідним у деяких випадках, воно часто безнадійно перевиконано. Це посилюється безглуздими показниками, нав'язуваними розробникам, на кшталт "100% покриття". http://www.rbcs-us.com/documents/Why-Most-Unit-Testing-is-Waste.pdf дає вагомий аргумент для цього. Розглянемо наступні проблеми при агресивному тестуванні блоку:

  • Безліч безглуздих тестів, які не підтверджують ділову цінність, а просто існують, щоб наблизитись до цього 100% охоплення. Там, де я працюю, ми мусимо писати одиничні тести для фабрик, які нічого іншого, крім створення нових примірників класів. Це не додає ніякої цінності. Або ті тривалі методи .equals (), що генеруються Eclipse - не потрібно їх перевіряти.
  • Щоб полегшити тестування, розробники поділили складні алгоритми на більш дрібні тестові одиниці. Звучить як виграш, правда? Ні, якщо вам потрібно відкрити 12 класів, щоб слідувати загальному кодовому шляху. У цих випадках тестування одиниць може фактично знизити читабельність коду. Ще одна проблема з цим полягає в тому, що якщо ви подрібните код на занадто малі шматочки, ви отримаєте величину класів (або фрагментів коду), які, здається, не мають жодних виправдань, крім того, що це підмножина іншого фрагмента коду.
  • Рефакторинг високо coveraged коду може бути важким, оскільки ви також повинні підтримувати велику кількість модульних тестів , які залежать від його роботи просто так . Це посилюється тестами поведінкових одиниць, де частина вашого тесту також перевіряє взаємодію співробітників класу (як правило, знущаються над ними).

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

Дуже багато магазинів випили TDD-допомогу, але як показує посилання вище, ряд досліджень показує, що її користь є непереконливою.


10
+1 для точки рефакторингу. Раніше я працював над застарілим продуктом, який латкували і змішували більше десяти років. Просто спроба визначити залежності для певного методу може зайняти більшу частину дня, а потім спроба з'ясувати, як знущатися над ними може зайняти ще більше часу. Для непересічної зміни в п’ять рядків потрібно понад 200 рядків тестового коду, а на виготовлення потрібно більше часу.
TMN

3
Це. У тесті відповідей MainMa 4 не слід робити (поза академічним контекстом), тому що подумайте, як це відбуватиметься на практиці ... якщо ім'я людини близьке до максимального розміру для рядка, щось пішло не так. Не тестуйте, у більшості випадків немає кодового шляху для його виявлення. Відповідна відповідь полягає в тому, щоб дозволити рамці викинути основні винятки з пам'яті, тому що в цьому полягає власне проблема.
Móż

3
Я вітаю вас, поки "не потрібно перевіряти ті тривалі .equals()методи, породжені Eclipse". Я написав тестовий ремінь для equals()і compareTo() github.com/GlenKPeterson/TestUtils Практично не вистачало будь-якої реалізації, яку я коли-небудь перевіряв. Як використовувати колекції якщо такі equals()і hashCode()не працюють разом правильно і ефективно? Я знову підбадьорюю решту вашої відповіді і вже проголосував за це. Я навіть погоджуюсь, що деяким автоматично сформованим методам equals () може не знадобитися тестування, але у мене було так багато помилок з поганими реалізаціями, що це нервує мене.
GlenPeterson

1
@GlenPeterson Погоджуюся. Моя колега написала для цієї мети EqualsVerifier. Дивіться github.com/jqno/equalsverifier
Tohnmeister

@ Ӎσᶎ ні, вам все одно доведеться перевірити неприйнятний вклад, саме так люди знаходять подвиги безпеки.
gbjbaanb

11

Не можна узагальнити.

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

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


Це дійсно цінна точка зору. На це питання потрібно більше контексту, щоб отримати повний відповідь.
CLF

3

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

Це означає що:

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

(Це не враховує рефакторинг коду, який спрямований на те, щоб витрачати менше часу на написання наступного коду. Він збалансований тестовим рефакторингом, який спрямований на те, щоб витрачати менше часу на написання наступних тестів.)

Так, якщо ви говорите про написання тестів після факту, тоді ви витратите більше часу:

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

Чим ви витратите фактично написання коду.

Так що так, це очікуваний захід.


3

Я вважаю це найважливішою частиною.

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

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

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


2

Це може бути для багатьох людей, але це залежить.

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

  • Визначення результатів та входів (параметрів)
  • Названня конвенцій
  • Структура - куди покласти речі.
  • Просте старе мислення

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

Більшість програмістів пишуть код набагато довше, ніж тести, тому я б очікував, що більшість з них не будуть настільки вільними. Крім того, ви можете додати час, необхідний для розуміння та використання вашої тестової системи.

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

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


2

Чи нормально витрачати стільки, якщо не більше, часу на тести, ніж фактичний код?

Так. З деякими застереженнями.

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

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

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

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

У передмові Роя Ошерова «Мистецтво тестування одиниць» (Manning, 2009) автор визнає, що брав участь у проекті, який значною мірою не вдався через величезний тягар розвитку, накладений погано розробленими одиничними тестами, які доводилося підтримувати протягом тривалість зусиль з розробки. Отже, якщо ви виявляєте, що витрачаєте занадто багато часу, не роблячи нічого, крім підтримки своїх тестів, це не обов'язково означає, що ви на правильному шляху, оскільки це "нормально". Ваші зусилля по розробці, можливо, перейшли в нездоровий режим, де для врятування проекту може знадобитися радикальне переосмислення вашої методології тестування.


0

Чи нормально витрачати стільки, якщо не більше, часу на тести, ніж фактичний код?

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