Чи пишуть одиничні тести вручну, підтверджуючи приклад?


9

Ми знаємо, що написання тестів JUnit демонструє один конкретний шлях через ваш код.

Один із моїх однодумців прокоментував:

Ручне написання одиничних тестів є підтвердженням на прикладі .

Він виходив з фону Haskell, який має такі інструменти, як Quickcheck і здатність міркувати про поведінку програми з типами .

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

Моє запитання: чи пишуть одиничні тести вручну, підтверджуючи приклад?


3
Ні, не писати / використовувати тести. Стверджуючи, що ваші одиничні тести є доказом того, що в програмі немає нічого поганого, - це «Доказ за прикладом» (неправильне узагальнення). Тести не полягають у тому, що математично підтверджується правильність коду - тести за своєю природою є експериментальними перевірками. Це безпечна мережа, яка допомагає вам створити впевненість, розповідаючи щось про код. Але вам належить вибрати хорошу стратегію зондування коду, і ви є тим, хто повинен інтерпретувати, що це означає.
Філіп Мілованович

Відповіді:


10

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

Але хороші одиничні тести ніколи цього не роблять. Натомість вони займаються діапазонами та крайовими справами.

Наприклад, якби ви писали одиничні тести для функції абсолютного значення, яка приймає ціле число як вхід, вам не потрібно буде перевіряти всі можливі значення введення, щоб довести, що код працює. Щоб отримати комплексний тест, вам знадобиться лише п'ять значень: -1, 0, 1, а також значення max та min для цілого вхідного числа.

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


11
Тестер коду заходить у бар і замовляє пиво. 5 пива. -1 пива, MAX_VALUE пива, курка. нуль.
Ніл

2
"5 значень" - це чиста нісенітниця. Розглянемо як тривіальну функцію на кшталт int foo(int x) { return 1234/(x - 100); }. Також зауважте, що (залежно від того, що ви тестуєте) вам може знадобитися переконатися, що недійсний ("поза діапазоном") введення повертає правильні результати (наприклад, що `` find_thing (thing) `правильно повертає якийсь статус" не знайдено " якщо річ не знайдена).
Брендан

3
@Brendan: Немає нічого істотного в тому, що це п'ять значень; у моєму прикладі це просто п'ять значень. Ваш приклад має різну кількість тестів, оскільки ви тестуєте іншу функцію. Я не кажу, що кожна функція вимагає рівно п’яти тестів; ви це зробили з читання моєї відповіді.
Роберт Харві

1
Бібліотеки генерального тестування зазвичай краще перевіряти кращі випадки, ніж ви. Якщо, наприклад, ви використовували поплавці замість цілих чисел, ваша бібліотека буде також перевірити -Inf, Inf, NaN, 1e-100, -1e-100, -0, 2e200... Я б вважав за краще не робити ті всі вручну.
Говеркуч

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

8

Будь- яке тестування програмного забезпечення нагадує "Доказ на прикладі", а не лише тестування одиниць за допомогою такого інструменту, як JUnit. І це не нова мудрість, є цитата Дійкстри з 1960 року, яка говорить по суті те саме:

"Тестування показує наявність, а не відсутність помилок"

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

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

Таким чином, створені вручну тести нічим не гірші, ніж випадково генеровані тести, часто зовсім навпаки.


1
Будь-який практичний тестовий набір з використанням випадкової перевірки також матиме одиничні тести. (Технічно одиничні тести - це лише вироджений випадок випадкового тестування.) Ваша формулювання говорить про те, що рандомізовані тести важко досягти або що поєднувати рандомізоване тестування та одиничне тестування важко. Зазвичай це не так. На мою думку, однією з найбільших переваг рандомізованого тестування є те, що воно наполегливо заохочує писати тести як властивості щодо коду, які завжди повинні міститись. Я вважаю за краще, щоб ці властивості були чітко зазначені (і перевірені!), Ніж треба було б зробити їх деякими бальними тестами.
Дерек Елкінс виїхав

@DerekElkins: "важко" IMHO - неправильний термін. Випадкові тести потребують певних зусиль, і це зусилля, які скорочують доступний час для випробувань рукоділля (і якщо у вас люди лише слідують за гаслами, як той, що згадується у питанні, вони, ймовірно, взагалі не займаються рукоділлям). Просто викинути безліч випадкових даних тесту на фрагмент коду - це лише половина роботи, треба також створити очікувані результати для кожного з цих тестових входів. Для деяких сценаріїв це можна зробити автоматично. Для інших - ні.
Док Браун

Хоча, безумовно, є випадки, коли потрібна якась думка, щоб вибрати хороший розподіл, це, як правило, не є серйозним припиненням роботи. Ваш коментар говорить про те, що ви думаєте про це неправильно. Властивості, які ви пишете для рандомізованої перевірки, є тими ж властивостями, які ви писали б для перевірки моделі або для офіційного підтвердження. Дійсно, вони можуть бути і використовуються для всіх цих речей одночасно. Немає "очікуваних результатів", які також потрібно отримати. Натомість ви просто констатуєте майно, яке завжди має містити. Деякі приклади: 1) штовхати щось на стек і ...
Дерек Елкінс пішов

... тоді вискакувати повинно бути таким же, як нічого не робити; 2) будь-який клієнт із залишком понад 10000 доларів повинен отримати високу процентну ставку на баланс і лише тоді; 3) положення спрайту завжди знаходиться в обмежувальному вікні екрана. Деякі властивості цілком можуть відповідати точковим тестам, наприклад, "коли баланс становить 0 доларів США, попереджають нульовий баланс". Властивості є частковими специфікаціями з ідеалом отримання загальної специфікації. Якщо у вас складно придумати ці властивості, це означає, що вам незрозуміло, що таке специфікація, і часто це означає, що ви матимете труднощі при продумуванні хороших одиничних тестів.
Дерек Елкінс покинув SE

0

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

Генеральне тестування, як QuickCheck, справді добре підходить для широкого простору входів. Це також набагато краще для вирішення кращих справ, ніж ручні тести: бібліотеки генеративного тестування з цим будуть більш досвідчені, ніж ви. З іншого боку, вони розповідають лише про інваріанти, а не про конкретні результати. Тож для перевірки вашої програми отримують правильні результати, вам все одно потрібні деякі ручні тести, щоб перевірити, що насправді це foo(bar) = baz.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.