Методи тестування недійсних методів?


170

Який найкращий спосіб перевірити метод, який нічого не повертає? Зокрема в c #.

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


55
Будь ласка, не використовуйте термін "TDD", якщо це не те, що ви робите. Ви робите тестування, а не TDD. Якби ви робили TDD, у вас ніколи не виникне питання типу "як протестувати метод". Тест існував би спочатку, а потім постало питання: "як змусити цей тест пройти?" Але якщо ви БУДИ робити TDD, ваш код буде написаний для тесту (а не навпаки), і ви, по суті, відповісте на власне питання. Ваш код буде відформатований інакше в результаті TDD, і ця проблема ніколи не виникне. Просто уточнююча.
Суамер

Відповіді:


151

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

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

Імперативні методи - ви можете перевірити, чи завдання було виконано насправді. Перевірте, чи відбулися фактично зміни штату. напр

void DeductFromBalance( dAmount ) 

можна перевірити, перевіривши, чи баланс після цього повідомлення дійсно менший від початкового значення на dAmount

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

void OnAccountDebit( dAmount )  // emails account holder with info

можна перевірити, перевіривши, чи надсилається електронний лист

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

string[] ExamineLogFileForX( string sFileName );
void InsertStringsIntoDatabase( string[] );

Рядок [] можна легко перевірити, надавши першому методу фіктивний файл та очікувані рядки. Друга - трохи хитра. Ви можете або використовувати Mock (google або шукати stackoverflow в глузливих структурах) для імітації БД, або натискати на фактичну БД і перевірити, чи вставлені рядки в потрібне місце. Перевірте цю тему, щоб знайти гарні книги ... Я б рекомендував тестування прагматичних одиниць, якщо ви перебуваєте в хрусті.
У коді він би використовувався так

InsertStringsIntoDatabase( ExamineLogFileForX( "c:\OMG.log" ) );

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

2
@andy - залежить від вашого визначення поняття "інтеграційні тести". Імперативні методи зазвичай змінюють стан, тому ви можете бути перевірені одиничним тестом, який допитує стан об'єкта. Інформаційні методи можуть бути перевірені тестовим модулем, який підключає слухача макету / співпрацівнику, щоб переконатися, що випробуваний видає правильне сповіщення. Я думаю, що обидва можуть бути розумно перевірені за допомогою одиничних тестів.
Gishu

@і базу даних можна знущати / розділяти інтерфейсом доступу, таким чином дозволяючи перевірити дію за допомогою даних, переданих об'єкту макету.
Пітер Гейгер

62

Перевірте його побічні ефекти. Це включає:

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

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


31

Як завжди: протестуйте, що метод повинен зробити!

Чи повинна вона десь змінити глобальний стан (тью, запах коду!)?

Чи повинен він зайти в інтерфейс?

Чи повинен він кидати виняток, коли викликається з неправильними параметрами?

Чи повинен він кидати виняток, коли викликається з правильними параметрами?

Чи повинен це ...?


11

Недійсні типи повернення / Підпрограми - це стара новина. Я не зробив тип недійсного повернення (якщо я не був дуже лінивий) за 8 років (з моменту цієї відповіді, так що трохи раніше, ніж це питання було задано).

Замість методу типу:

public void SendEmailToCustomer()

Створіть метод, що відповідає парадигмі int.TryParse () Microsoft:

public bool TrySendEmailToCustomer()

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

Крім того, bool - не єдиний тип стану. Існує декілька разів, коли раніше зроблена Підпрограма могла фактично повернути три або більше різних станів (Добрий, Нормальний, Поганий тощо). У цих випадках ви просто використовуватимете

public StateEnum TrySendEmailToCustomer()

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

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

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

public <Constructor/MethodName> (IBusinessDataEtc otherLayerOrTierObject, string[] stuffToInsert)

Тепер, коли ви можете прийняти інтерфейс об'єкта свого бізнесу / рівня даних, ви можете знущатися над ним під час тестів модулів і не мати ніяких залежностей або страху перед тестуванням інтеграції "Випадкові".

Отже, у вашому реальному коді ви переходите в РЕАЛЬНИЙ IBusinessDataEtcоб’єкт. Але у вашому модульному тестуванні ви передаєте IBusinessDataEtcоб'єкт MOCK . У цьому макеті ви можете включити Неінтерфейсні властивості на кшталт int XMethodWasCalledCountабо щось, стан якого оновлюється під час виклику методів інтерфейсу.

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

  1. Стан "Підпрограми", який зараз є методом спробу парадигми.
  2. Стан вашого IBusinessDataEtcоб'єкта Mock .

Для отримання додаткової інформації про ідеї вприскування залежностей на рівні будівництва ... як вони стосуються тестування блоків ..., ознайомтеся з моделями дизайну будівельника. Він додає ще один інтерфейс і клас для кожного поточного інтерфейсу / класу, який у вас є, але вони дуже крихітні і забезпечують ВЕЛИЧЕЗНІ функціональні можливості для кращого тестування модулів.


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

чи не повинно бути щось подібне public void sendEmailToCustomer() throws UndeliveredMailException?
А.Емад

1
@ A.Emad Добре запитання, але ні. Контроль потоку коду, покладаючись на метання винятків, - давно відома погана практика. Хоча в voidметодах, особливо в об'єктно-орієнтованих мовах, це був єдиний реальний варіант. Найдавнішою альтернативою Microsoft є парадигма Try, яку я обговорюю, і парадигми функціонального стилю, такі як Monads / Maybes. Таким чином, Команди (In CQS) все ще можуть повертати цінну інформацію про стан, а не покладатися на метання, що схоже на GOTO(Що ми знаємо, що погано). метання (і goto) - повільні, важкі для налагодження, і це не дуже добра практика.
Суамер

Дякую, що ви очистили це. Це специфічно для C # чи погана практика взагалі кидати винятки навіть у таких мовах, як Java та C ++?
А.Емад

9

Спробуйте це:

[TestMethod]
public void TestSomething()
{
    try
    {
        YourMethodCall();
        Assert.IsTrue(true);
    }
    catch {
        Assert.IsTrue(false);
    }
}

1
Це не повинно бути необхідним, але можна зробити так
Натан Алард

Ласкаво просимо до StackOverflow! Будь ласка, подумайте, щоб додати до свого коду пояснення. Дякую.
Аврасфера

2
Розроблений ExpectedAttribute, щоб зробити цей тест більш чітким.
Мартін Ліверсанс

8

Можна навіть спробувати так:

[TestMethod]
public void ReadFiles()
{
    try
    {
        Read();
        return; // indicates success
    }
    catch (Exception ex)
    {
        Assert.Fail(ex.Message);
    }
}

1
Це найпростіший спосіб, я думаю.
Навін Пандіт

5

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


4

Імовірно, метод щось робить, а не просто повертається?

Якщо припустити, що це так, то:

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

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


3

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


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

2

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


Погоджено - перевірка поведінки макетів тестування методу була б одним із способів.
Джефф Шумахер

0

Що коли-небудь екземпляр ви використовуєте для виклику методу void, ви можете просто використовувати,Verfiy

Наприклад:

У моєму випадку _Logце екземпляр і LogMessageце метод, який потрібно перевірити:

try
{
    this._log.Verify(x => x.LogMessage(Logger.WillisLogLevel.Info, Logger.WillisLogger.Usage, "Created the Student with name as"), "Failure");
}
Catch 
{
    Assert.IsFalse(ex is Moq.MockException);
}

Чи є Verifyвикиди винятком через невдачу методу, тест не зможе?

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