Що слід перевірити за допомогою одиничних тестів?


122

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

Проблема в тому, що я не знаю, що тестувати. Чи слід перевірити поширений випадок? Крайовий корпус? Як я можу знати, що функція належним чином охоплена?

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


Погляньте на блог Роя Ошерова . Тут є багато інформації про тестування одиниць, включаючи відео. Він також написав книгу "Мистецтво одиничного тестування", що дуже добре.
Пірс Майєрс

9
Цікаво, що ти думаєш про це після майже 5 років? Тому що все більше і більше я відчуваю, що люди повинні краще знати "чого не робити тестування" в наш час. Розвиток поведінки розвинувся з питань, як ви задавали.
Remigijus Pankevičius

Відповіді:


121

Моя особиста філософія таким чином була:

  1. Перевірте загальний випадок всього, що ви можете. Це підкаже вам, коли цей код порушиться після того, як ви внесете деякі зміни (це, на мою думку, найбільша перевага автоматизованого тестування одиниць).
  2. Перевірте кращі випадки кількох незвично складних кодів, які, на вашу думку, можуть мати помилки.
  3. Щоразу, коли ви знайдете помилку, пишіть тестовий випадок, щоб прикрити його, перш ніж виправити
  4. Додайте кращі тести до менш критичного коду, коли хтось встигає вбити.

1
Дякую за це, я сюди перелітав з тими ж питаннями, що і ОП.
Стівен

5
+1, хоча я також перевіряв кращі випадки будь-яких функцій бібліотеки / утиліти, щоб переконатися, що у вас є логічний API. наприклад, що відбувається при передачі нуля? як щодо порожнього вводу? Це допоможе переконатися, що ваш дизайн є логічним та задокументує поведінку кутового випадку.
mikera

7
# 3 здається дуже надійною відповіддю, оскільки це приклад із реального життя, як могло б допомогти одиничне тестування. Якщо він зламався один раз, він може зламатися знову.
Райан Гріффіт

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

Ваша відповідь була представлена ​​в цій популярній середній статті: hackernoon.com/…
BugHunterUK

67

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

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

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

| # |  Equivalence class    | Input        | Output       | # test cases |
+------------------------------------------------------------------------+
| 1 | Lowercase letter      | a - z        | A - Z        | 26           |
| 2 | Uppercase letter      | A - Z        | A - Z        | 26           |
| 3 | Non-alphabetic chars  | 0-9!@#,/"... | 0-9!@#,/"... | 42           |
| 4 | Non-printable chars   | ^C,^S,TAB... | ^C,^S,TAB... | 34           |

Останній стовпець повідомляє про кількість тестових випадків, якщо перерахувати їх усі. Технічно, за правилом @ fishtoaster 1, ви б включили 52 тестові випадки - усі ці перші два ряди, наведені вище, підпадають під "загальний випадок". @ правило fishtoaster 2 також додасть частину або все з рядків 3 і 4 вище. Але для тестування розділення на еквівалентність достатньо будь-якого одного тестового випадку у кожному класі еквівалентності. Якщо ви вибрали "a" або "g" або "w", ви протестуєте той самий шлях коду. Таким чином, у вас загалом 4 тестові справи замість 52+.

Аналіз граничних значень рекомендує невелике уточнення: по суті, це говорить про те, що не кожен член класу еквівалентності є, безумовно, еквівалентним. Тобто, значення на кордонах також слід вважати гідними тестового випадку самостійно. (Одне з простих обґрунтувань цього - сумнозвісна помилка один за одним !) Таким чином, для кожного класу еквівалентності ви могли мати 3 тестових введення. Дивлячись на вхідний домен вище - і маючи певні знання про значення ASCII - я можу придумати такі входи тестового випадку:

| # | Input                | # test cases |
| 1 | a, w, z              | 3            |
| 2 | A, E, Z              | 3            |
| 3 | 0, 5, 9, !, @, *, ~  | 7            |
| 4 | nul, esc, space, del | 4            |

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


3
+1 Саме так я інтуїтивно пишу свої тести. Тепер я можу вказати ім'я :) Дякую за те, що поділився цим.
guillaume31

+1 за "якісні відповіді корисні, але можливо - і краще - бути кількісними"
Джиммі Брек-Маккі

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

18

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

Я пропоную вам написати тести на речі, у яких у вас кишечник погано, або на речі, які є дуже важливими та / або елементарними. Тести блоку IMHO не є заміною для хорошого інженерного та захисного кодування. В даний час я працюю над проектом, який є більш-менш непридатним. Це дійсно стабільно, але болить рефактор. Насправді ніхто не торкався цього коду за один рік, а стек програмного забезпечення, на якому він базується, становить 4 роки. Чому? Оскільки він переповнений одиничними тестами, якщо бути точним: Тестові одиниці та автоматизовані тести інтеграції. (Коли-небудь чули про огірок тощо?) І ось найкраща частина: Цей (поки що) непридатний програмний продукт розроблений компанією, працівники якої є піонерами на тестовій сцені розвитку. : D

Тому моя пропозиція:

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

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

  • Залиште цю інформацію про TDD-експертам.

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


2
Чи можете ви детальніше розповісти, чому тести роблять це програмне забезпечення болем для рефактора?
Майк Партрідж

6
Великий +1. Якщо стіни одиничних тестів, які перевіряють реалізацію замість правил, вносять будь-які зміни, потрібно
2-3 рази

9
Як і погано написаний виробничий код, погано написані одиничні тести важко підтримувати. "Занадто багато тестових одиниць" звучить як невміння залишатися ДУХОМ; кожен тест повинен вирішувати / доводити конкретну частину системи.
Аллан

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

1
-1: Я думаю, що цей пост написаний погано. Згадано багато речей, і я не знаю, як вони всі співвідносяться. Якщо сенс відповіді "бути економічним", то як ваш приклад взагалі співвідноситься? Це здається, що у вашому прикладі ситуації (хоча і реальної) є погані одиничні тести. Поясніть, будь ласка, які уроки я маю з цього вивчити і як це допомагає мені бути економним. Крім того, я, чесно кажучи, просто не знаю, що ви маєте на увазі, коли говорите Leave this TDD stuff to the TDD-experts.
Олександр Птах

8

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

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


Як ви обчислюєте цей відсоток покриття? Що означає покривати 90% свого коду?
zneak

2
@zneak: є інструменти покриття коду, які обчислять їх для вас. Швидкий google для "покриття коду" повинен відобразити їхню кількість. Інструмент відстежує рядки коду, які виконуються під час виконання тестів, і бази, які відповідають загальним рядкам коду в складі (-ях), щоб визначити відсоток покриття.
Стівен Еверс

-1. Не відповідає на питання:The problem is, I don't know _what_ to test
Олександр Птах

6

Якщо почати наступний Test Driven розвитку практики, вони будуть свого роду керівництво вас через процес і знаючи , що тестування буде, природно. Деякі місця для початку:

Тести приходять першими

Ніколи, ніколи не пишіть код перед тим, як писати тести. Пояснення див. У розділі Червоний-Зелений-Рефактор-повтор .

Напишіть регресійні тести

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

Червоний-Зелений-Refactor-Повторіть

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

Зелений : Напишіть найпростіший і дурний код, який насправді робить тестовий прохід. Не намагайтеся бути розумними. Навіть якщо ви бачите, що є очевидний крайовий випадок, але тест враховуйте, не пишіть код, щоб обробити його (але не забувайте про крайовий регістр: він вам знадобиться пізніше). Ідея полягає в тому, щоб кожен фрагмент коду, який ви пишете, кожен if, кожен try: ... except: ...повинен бути виправданий тестовим випадком. Код не повинен бути елегантним, швидким або оптимізованим. Ви просто хочете, щоб тест пройшов.

Refactor : Очистіть код, підберіть правильні назви методів. Подивіться, чи тест ще проходить. Оптимізуйте. Виконайте тест ще раз.

Повторіть : Ви пам’ятаєте крайню справу, яку тест не охоплював, правда? Отже, зараз настав його великий момент. Напишіть контрольну скриньку, яка висвітлює цю ситуацію, стежте за тим, як вона виходить з ладу, напишіть якийсь код, побачите, як він проходить, рефактор.

Перевірте свій код

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


1
Якщо я вже маю існуючу і (наскільки я бачу) робочу базу коду, що робити?
zneak

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

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

1
Якщо ви торкаєтесь її, протестуйте. І Clean Code (Роберт Мартін) пропонує вам написати "тести на навчання" для коду сторонніх розробників. Таким чином ви навчитеся ним користуватися, і у вас є тести на випадок, якщо нова версія змінить поведінку, яку ви використовуєте.
Роджер Віллокс

3

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

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


0

Відповідь акцій полягає в тому, щоб "перевірити все, що могло зламати" .

Що занадто просто зламати? Поля даних, мозок-мертві приладдя власності та подібні накладні шаблони. Можливо, будь-що інше реалізує певну частину вимоги, яка може бути ідентифікована, і може бути корисною для перевірки.

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


Добре. Отже, які випадки слід перевірити? "Нормальний" випадок? Крайовий корпус?
zneak

3
Практичне правило? Один-два праворуч до середини золотої стежки, а також всередині та трохи поза будь-якими краями.
Джефрі Хантін

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