Як я можу використовувати Assert.Throws для затвердження типу виключення?


247

Як я можу Assert.Throwsстверджувати тип виключення та власне формулювання повідомлення.

Щось на зразок цього:

Assert.Throws<Exception>(
    ()=>user.MakeUserActive()).WithMessage("Actual exception message")

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

Відповіді:


444

Assert.Throws повертає викинутий виняток, який дозволяє стверджувати про виключення.

var ex = Assert.Throws<Exception>(() => user.MakeUserActive());
Assert.That(ex.Message, Is.EqualTo("Actual exception message"));

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

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

var ex = Assert.Throws<ArgumentNullException>(() => foo.Bar(null));
Assert.That(ex.ParamName, Is.EqualTo("bar"));

Ви також можете використовувати API для вільного виконання цих тверджень:

Assert.That(() => foo.Bar(null), 
Throws.Exception
  .TypeOf<ArgumentNullException>()
  .With.Property("ParamName")
  .EqualTo("bar"));

або альтернативно

Assert.That(
    Assert.Throws<ArgumentNullException>(() =>
        foo.Bar(null)
    .ParamName,
Is.EqualTo("bar"));

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


Це було дуже корисно для мене - я хотів, щоб спосіб відобразити помилку, я навіть не читав, чи було повернено значення методом Assert.Throws. Спасибі
Haroon

6
+1 Дякую, що показали API Fluent, чомусь у мене виникли проблеми з розумінням, як ним користуватися лише з документів NUnit.
aolszowka

Коли ви хочете стверджувати повідомлення, ви можете також безпосередньо використовувати властивість повідомлення замість "Властивості".
Марсель

25

Тепер ви можете використовувати ExpectedExceptionатрибути, наприклад

[Test]
[ExpectedException(typeof(InvalidOperationException), 
 ExpectedMessage="You can't do that!"]
public void MethodA_WithNull_ThrowsInvalidOperationException()
{
    MethodA(null);
}

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

14
+1 також хороший спосіб перевірити на винятки. Єдине, що потрібно пам’ятати про це, це те, що теоретично будь-який рядок коду, що передає InvalidOperationException з цим повідомленням, пройде тест, включаючи код у вашому тесті, який готує дані / об'єкти тесту або будь-який інший метод, який, можливо, потрібно буде виконати перед те, що вам цікаво тестувати, можливо, це призведе до помилкового позитиву. Звичайно, це залежить від конкретного повідомлення та типу винятку, який ви тестуєте. З Assert.Throwможна орієнтувати точну лінію ви зацікавлені в.
Нема

21
Атрибут очікуваного розширення
Frank Sebastià

13
Assert.That(myTestDelegate, Throws.ArgumentException
    .With.Property("Message").EqualTo("your argument is invalid."));

2
Зі вступом оператора nameof оператор я би відредагував цього чудового прихильника на:Assert.That(myTestDelegate, Throws.ArgumentException .With.Property(nameof(ArgumentException.Message)).EqualTo("your argument is invalid."));
Самуель

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

1
Я повністю згоден з вами щодо Exception.Message. Я все-таки рекомендую принаймні додати цю альтернативу, оскільки With.Propertyїї можна використовувати і на інших об'єктах, що в цьому випадку покращило б стабільність коду.
Самуїл

5

Це старе, але відповідне питання із застарілими відповідями, тому я додаю поточне рішення:

public void Test() {
    throw new MyCustomException("You can't do that!");
}

[TestMethod]
public void ThisWillPassIfExceptionThrown()
{
    var exception = Assert.ThrowsException<MyCustomException>(
        () => Test(),
        "This should have thrown!");
    Assert.AreEqual("You can't do that!", exception.Message);
}

Це працює з using Microsoft.VisualStudio.TestTools.UnitTesting;


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

3

Щоб розширити стійку відповідь і надати більше функціоналу NUnit, ви можете зробити це:

public bool AssertThrows<TException>(
    Action action,
    Func<TException, bool> exceptionCondition = null)
    where TException : Exception 
{
    try
    {
        action();
    }
    catch (TException ex)
    {
        if (exceptionCondition != null)
        {
            return exceptionCondition(ex);
        }

        return true;
    }
    catch
    {
        return false;
    }

    return false; 
}

Приклади:

// No exception thrown - test fails.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => {}));

// Wrong exception thrown - test fails.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => { throw new ApplicationException(); }));

// Correct exception thrown - test passes.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => { throw new InvalidOperationException(); }));

// Correct exception thrown, but wrong message - test fails.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => { throw new InvalidOperationException("ABCD"); },
        ex => ex.Message == "1234"));

// Correct exception thrown, with correct message - test passes.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => { throw new InvalidOperationException("1234"); },
        ex => ex.Message == "1234"));

2

Я давно минув, коли це питання було порушено, я розумію, але нещодавно я зіткнувся з тим же, і пропоную цю функцію для MSTest:

public bool AssertThrows(Action action) where T : Exception 
{ 
try {action();} 
catch(Exception exception) 
{ 
    if (exception.GetType() == typeof(T)) return true; 
} 
return false; 
}

використання:

Assert.IsTrue(AssertThrows<FormatException>(delegate{ newMyMethod(MyParameter); }));

Більше тут: http://phejndorf.wordpress.com/2011/02/21/assert-that-a-particular-exception-has-ocured/


2

Оскільки мене турбує багатослівність деяких нових моделей NUnit, я використовую щось подібне, щоб створити чистіший для мене код:

public void AssertBusinessRuleException(TestDelegate code, string expectedMessage)
{
    var ex = Assert.Throws<BusinessRuleException>(code);
    Assert.AreEqual(ex.Message, expectedMessage);
}

public void AssertException<T>(TestDelegate code, string expectedMessage) where T:Exception
{
    var ex = Assert.Throws<T>(code);
    Assert.AreEqual(ex.Message, expectedMessage);
}

Потім використання:

AssertBusinessRuleException(() => user.MakeUserActive(), "Actual exception message");

1
Що таке TestDelegate?
reggaeguitar

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