Як мені написати тест на чистий метод, який нічого не повертає?


13

У мене є купа класів, які займаються валідацією значень. Наприклад, RangeValidatorклас перевіряє, чи є значення у вказаному діапазоні.

Кожен клас валідатора містить два способи: is_valid(value)який повертає Trueабо Falseзалежно від значення, і ensure_valid(value)який перевіряє вказане значення, або не робить нічого, якщо значення дійсне, або викидає конкретний виняток, якщо значення не відповідає заданим правилам.

Наразі з цим методом пов’язано два одиничні тести:

  • Той, який передає недійсне значення, і забезпечує викид виключення.

    def test_outside_range(self):
        with self.assertRaises(demo.ValidationException):
            demo.RangeValidator(0, 100).ensure_valid(-5)
    
  • Той, який передає дійсне значення.

    def test_in_range(self):
        demo.RangeValidator(0, 100).ensure_valid(25)
    

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

Це сучасна практика при методах тестування, які не повертають значення і не мають побічних ефектів? Або я повинен переписати тест по-іншому? Або просто покласти коментар, що пояснює, що я роблю?



11
Педантична точка, але якщо у вас є функція, яка не бере аргументів (збережіть для selfдовідки) і не повертає результату, це не є чистою функцією.
Девід Арно

10
@DavidArno: Це не педантичний момент, це прямо в основі питання: метод важко перевірити саме тому, що він нечистий.
Йорг W Міттаг

@DavidArno Більш педантична точка, у вас може бути метод "нічого не робити" (припускаючи, що "не повертає результатів" інтерпретується як "повертає тип одиниці", який називається voidбагатьма мовами і має нерозумні правила.) Або ви можете мати нескінченну кількість цикл (який працює навіть тоді, коли "не повертає результату" насправді означає, що результатів немає)
Дерек Елкінс пішов SE

Є будь-яка чиста функція типу unit -> unitна будь-якій мові, яка вважає одиницю стандартним типом даних замість чогось магічного спеціального випадку. На жаль, він просто повертає одиницю і більше нічого не робить.
Phoshi

Відповіді:


21

Більшість тестових фреймворків мають чітке твердження "Не кидає", наприклад, у Жасмін є, expect(() => {}).not.toThrow();а також nUnit та друзі.


1
І якщо в тестовій структурі немає такого твердження, завжди можна створити локальний метод, який робить точно те саме, що робить код само собою зрозумілим. Гарна ідея.
Арсеній Муренко

7

Це сильно залежить від мови та використовуваних рамок. Якщо говорити з точки зору NUnit, існують Assert.Throws(...)методи. Ви можете передати їм лямбда-метод:

Assert.Throws(() => rangeValidator.EnsureValid(-5))

який виконується в межах Assert.Throws. Виклик лямбда, швидше за все, буде обгорнуто try { } catch { }блоком, і твердження не вдасться, якщо буде винято виняток.

Якщо у вашій рамці не передбачено цих засобів, ви можете їх вирішити, загорнувши виклик власноруч (я пишу на C #):

// assert that the method does not fail
try
{
    rangeValidator.EnsureValid(50);
}
catch(Exception e)
{
    Assert.IsTrue(false, $"Exception: {e.Message}");
}

// assert that the method does fail
try
{
    rangeValidator.EnsureValid(50);
    Assert.IsTrue(false, $"Method is expected to throw an exception");
}
catch(Exception e)
{
}

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

Редагувати

Як зазначив Док Браун у коментарях, питання не було вказувати на те, що метод кидає, а що він не кидає. У NUnit також є твердження про це

Assert.DoesNotThrow(() => rangeValidator.EnsureValid(-5))

1

Просто додайте коментар, щоб зрозуміти, чому не потрібно стверджувати, і чому ви його не забули.

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

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


1

Ви також можете стверджувати, що деякі методи викликаються (або не викликаються) належним чином.

Наприклад:

public void SendEmail(User user)
     ... construct email ...
     _emailSender.Send(email);

У вашому тесті:

emailSenderMock.VerifyIgnoreArgs(service =>
    service.Send(It.IsAny<Email>()),
    Times.Once
);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.