Как я могу использовать Assert.Throws, чтобы утверждать тип исключения?

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

Что-то вроде этого:

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

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

Ответ 1

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"));

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

Ответ 2

Теперь вы можете использовать атрибуты ExpectedException, например.

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

Ответ 3

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

Ответ 4

Чтобы расширить постоянный ответ и предоставить больше возможностей 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"));

Ответ 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;

Ответ 6

Это долгое время, когда эта проблема была поднята, я понимаю, но недавно я столкнулся с тем же, и предложил эту функцию для 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-occured/

Ответ 7

Поскольку меня беспокоит многословие некоторых новых шаблонов 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(() => service.Create(content), "Name already exists");