Як ви протестуєте приватні методи з NUnit?


104

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

Як ви протестуєте приватні методи з NUnit?

Відповіді:


74

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

Отже, NUnit не забезпечує жодного механізму тестування непублічних членів.


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

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

3
@Quango, у стандартній тестовій установці, "автор" є виробничим кодом. Тим не менш, якщо ви не відчуваєте проблеми з дизайном, у вас є варіанти тестування непублічних методів без зміни класу. System.Reflectionдозволяє отримувати доступ та не викликати загальнодоступні методи, використовуючи прапори прив'язки, так що ви можете зламати NUnit або налаштувати власний фреймворк. Або (простіше, я думаю), ви можете встановити прапор часу компіляції (#if TESTING), щоб змінити модифікатори доступу, що дозволяє використовувати існуючі рамки.
harpo

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

6
@AndersForsgren Але суть одиничного тестування, на відміну від інтеграції чи функціонального тестування, полягає саме в тестуванні таких деталей. А покриття коду вважається важливою метрикою тестування одиниць, тому теоретично кожен фрагмент логіки "достатньо просунутий, щоб підлягати тестуванню".
Кайл Странд

57

Хоча я погоджуюся, що в центрі уваги тестування одиниць повинен бути публічний інтерфейс, ви отримуєте набагато більш детальне враження про свій код, якщо ви також перевірите приватні методи. Рамка тестування MS дозволяє це зробити за допомогою використання PrivateObject і PrivateType, NUnit не робить цього. Що я роблю замість цього:

private MethodInfo GetMethod(string methodName)
{
    if (string.IsNullOrWhiteSpace(methodName))
        Assert.Fail("methodName cannot be null or whitespace");

    var method = this.objectUnderTest.GetType()
        .GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);

    if (method == null)
        Assert.Fail(string.Format("{0} method not found", methodName));

    return method;
}

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


7
Тестування цих маленьких допоміжних методів фіксує частину підрозділу в одиничному тестуванні. Не всі також підходять для корисних класів.
Йохан Ларссон

12
Це саме те, що мені було потрібно. Дякую! Все, що мені потрібно було, це перевірити, чи приватне поле налаштоване належним чином, і мені НЕ потрібно витрачати 3 години, щоб зрозуміти, як це визначити через загальнодоступний інтерфейс. Це існуючий код, який неможливо відновити за допомогою примхи. Смішно, як на просте запитання можна відповісти ідеалістичною догмою ...
Droj

5
Це має бути обрана відповідь, оскільки це єдина, яка насправді відповідає на питання.
bavaza

6
Домовились. Обрана відповідь має свої достоїнства, але це відповідь на "чи слід перевірити приватні методи", а не на питання ОП.
честербр

2
працює. Дякую! Це те, що шукають багато людей. Це повинна бути обрана відповідь,
Able Johnson

47

Загальна модель для написання одиничних тестів - це лише перевірка публічних методів.

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

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

Можливо, правильно перенести їх у клас помічників і оприлюднити їх там. Ваш клас може не піддаватися API.

Таким чином тестовий код ніколи не змішується з вашим відкритим кодом.

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


1
+ 1 так! Я щойно прийшов до такого висновку, коли писав свої тести, гарні поради!
Енді

1
Переміщення приватних методів, які ви хочете перевірити, але не піддавати користувачам API на інший клас, який не піддається впливу, та оприлюднити їх там саме те, що я шукав.
Thomas N

дякую @morechilli Ніколи раніше не бачив InternalsVisibleTo. Зробити тестування набагато простіше.
Ендрю Макнафтон

Привіт. Нещодавно отримав декілька голосів, що не мають коментарів. Надайте відгуки, щоб пояснити свої думки чи проблеми.
morechilli

6
Отже, якщо у вас є приватний метод, який викликається в декількох місцях класу, щоб зробити щось дуже конкретне, необхідне лише цьому класу, і його не можна виставляти як частину загальнодоступного API ... Ви кажете поставити його в помічник клас просто для тестування? Ви можете просто оприлюднити його для класу.
Даніель Макіас

21

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

http://msdn.microsoft.com/en-us/library/0tke9fxk.aspx

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

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

Якщо ви все ще відчуваєте необхідність перевірити внутрішню, то, можливо, ви також хочете розглянути глузування під час тестування, оскільки ви, як правило, хочете зосередитись на одному фрагменті коду. Знущання - це те, коли ти вводиш у нього залежність від об'єктів, але об'єкти, які вводяться, не є "реальними" або виробничими об'єктами. Це фіктивні об'єкти з жорстко кодованою поведінкою, щоб полегшити виділення поведінкових помилок. Rhino.Mocks - популярний фреймворк для вільних глузувань, який по суті буде писати об’єкти для вас. TypeMock.NET (комерційний продукт із доступною версією для спільноти) є більш потужною основою, яка може знущатися з об’єктів CLR. Дуже корисно для знущань із класів SqlConnection / SqlCommand і Datatable, наприклад, при тестуванні додатка бази даних.

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


12
Можна перевірити внутрішні методи, не приватні (з NUnit).
TrueWill

6

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

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


5

Це питання є в минулі роки, але я думав, що поділюсь своїм способом цього.

В основному, у мене є всі мої тестові класи в збірці, яку вони тестують у просторі імен 'UnitTest' нижче 'за замовчуванням' для цієї збірки - кожен тестовий файл загортається у:

#if DEBUG

...test code...

#endif

блок, і все це означає, що а) він не поширюється у випуску і б) я можу використовувати internal/ Friendдекларації рівня без стрибків з обручем.

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

public partial class TheClassBeingTested
{
    private int TheMethodToBeTested() { return -1; }
}

в основних класах складання та тестовому класі:

#if DEBUG

using NUnit.Framework;

public partial class TheClassBeingTested
{
    internal int NUnit_TheMethodToBeTested()
    {
        return TheMethodToBeTested();
    }
}

[TestFixture]
public class ClassTests
{
    [Test]
    public void TestMethod()
    {
        var tc = new TheClassBeingTested();
        Assert.That(tc.NUnit_TheMethodToBeTested(), Is.EqualTo(-1));
    }
}

#endif

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


4

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


3

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

Для проекту, над яким я працюю лише над MSTest (на жаль), мабуть, існує спосіб, використовуючи аксесуари, для тестування приватних методів.


2

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

Ви просто не повинні перевіряти те, що не є загальнодоступним.

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

Якщо ваш клієнт може запустити тестовий набір і побачити, що наданий вами код насправді "працює", я не вважаю це проблемою (доки ви не віддасте свій IP-адресу через це). Що я включаю до кожного випуску - це тестові звіти та звіти про висвітлення коду.


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

1

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

Тож якщо ви дійсно хочете протестувати внутрішніх членів, можете скористатися одним із таких підходів:

  1. Робіть свого члена публічним. У багатьох книгах автори пропонують такий підхід як простий
  2. Ви можете зробити членів внутрішніми і додати InternalVisibleTo до assebly
  3. Ви можете зробити учасників класу захищеними та успадкувати ваш тестовий клас від вашого класу, який перевіряється.

Приклад коду (псевдокод):

public class SomeClass
{
    protected int SomeMethod() {}
}
[TestFixture]
public class TestClass : SomeClass{

    protected void SomeMethod2() {}
    [Test]
    public void SomeMethodTest() { SomeMethod2(); }
}

Здається, що тарган захований у вашому прикладі коду;) Метод всередині кріплення NUnit повинен бути відкритим, інакше ви отримаєте Message: Method is not public.
Gucu112

@ Gucu112 Дякую Я полагодив це. Взагалі це не має значення, оскільки мета - показати підхід до дизайну pov.
буржуй

1

Ви можете зробити свої методи захищеними внутрішніми, а потім використовуючи assembly: InternalsVisibleTo("NAMESPACE") тестовий простір імен.

Значить, НІ! Ви не можете отримати доступ до приватних методів, але це обхід.


0

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

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

Я хлопець на Java, і тому вигляд пакунків у C # можна назвати зовсім іншим. Досить сказати, що для доступу до цих методів два класи повинні знаходитися в одному просторі імен.

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