Тести розбиття одиниць за вимогою чи методом


17

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

public void HandleItem(item a)
{         
     CreateNewItem();
     UpdateStatusOnPreviousItem();
     SetNextRunDate();
}

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

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

public void GivenItem_WhenRun_Thenxxxxx
{
     HandleItem(item);
     // Assert item has been created
     // Assert status has been set on the previous item
     // Assert run date has been set
}

Але я подумав, що також можу написати це як три окремі тести:

public void GivenItem_WhenRun_ThenItemIsCreated()
{
    HandleItem(item);
}

public void GivenItem_WhenRun_ThenStatusIsUpdatedOnPreviousItem()
{
   HandleItem(item);
}

public void GivenItem_WhenRun_ThenRunDateIsSet()
{
     HandleItem(item);
}

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

Чи є рекомендований підхід до цього?

Спасибі

Відповіді:


30

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

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

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


2
+1, хороший бал. Мені не прийшло в голову, що час побудови також може бути вузьким місцем.
Кіліан Фот

1
@KilianFoth: Ти не працюєш на C ++ досить часто :(
Матьє М.

1
@MatthieuM. Якщо чесно, питання позначено "C #"
Doc Brown

10

Коротка відповідь: набагато важливіше, щоб ваші тести покривали всю функціональність, ніж те, як вони це роблять.

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

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

3

Використовуйте один виклик методу з декількома твердженнями. Ось чому:

Під час тестування HandleItem (a) ви перевіряєте, що метод привів елемент у правильний стан. Замість "одного твердження за тест" подумайте "одне логічне поняття на тест".

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

З декількома твердженнями (з відповідними повідомленнями) ви точно знатимете, що не вдалося. Як правило, випробовуєте метод кілька разів для кількох входів або станів введення, щоб не уникнути кількох тверджень.

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

Ви можете розглянути можливість оприлюднення інших 3 методів та тестування їх незалежно. Або навіть вилучення їх до іншого класу.


0

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


-1

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


-2

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

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

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