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

Как я могу использовать Assert (или другой класс Test?), Чтобы проверить, что было сгенерировано исключение?

 bytedev03 окт. 2016 г., 16:16
 Kevin Pullin01 июн. 2009 г., 07:04
Какую систему модульного тестирования вы используете?
 shahkalpesh01 июн. 2009 г., 07:10
Не помогает атрибут ExpectedException? ссылка:msdn.microsoft.com/en-us/library/...
 Alex01 июн. 2009 г., 07:05
Visual Studio Интегрированный
 dfjacobs01 июн. 2009 г., 07:12
Забавно, я только что закончил искать ответ на это, нашел его вstackoverflow.com/questions/741029/testing-exceptions.

Ответы на вопрос(22)

NFluentкоторыйускоряет и облегчает способ написания ваших утверждений.

Довольно просто написать утверждение для выброса исключения:

    [Test]
    public void given_when_then()
    {
        Check.ThatCode(() => MethodToTest())
            .Throws<Exception>()
            .WithMessage("Process has been failed");
    }

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

[ExpectedException(typeof(ArgumentException))]

если вы просто хотите убедиться, что выбрано «любое исключение», но не знаете тип, вы можете использовать catch all:

[TestMethod]
[ExpectedException(typeof(Exception), AllowDerivedTypes = true)]
public void ThrowExceptionTest()
{
    //...
}

В случае использованияNUnit, попробуй это:

Assert.That(() =>
        {
            Your_Method_To_Test();
        }, Throws.TypeOf<Your_Specific_Exception>().With.Message.EqualTo("Your_Specific_Message"));

я, которые фактически добавляются в следующих двух строках теста.

var testDelegate = () => MyService.Method(params);
Assert.Throws<Exception>(testDelegate);

Assert.Throws<ExpectedException>(() => methodToTest());


Также возможно сохранить выброшенное исключение для дальнейшей его проверки:

ExpectedException ex = Assert.Throws<ExpectedException>(() => methodToTest());
Assert.AreEqual( "Expected message text.", ex.Message );
Assert.AreEqual( 5, ex.SomeNumber);

Видеть:http://nunit.org/docs/2.5/exceptionAsserts.html

 Mike Lowery18 нояб. 2017 г., 05:29
Это также работает с MSTest.
Решение Вопроса

что вы применяете атрибут ExpectedException к методу теста.

Образец из документации здесь:Пошаговое руководство по модульному тестированию с Visual Studio Team Test

[TestMethod]
[ExpectedException(typeof(ArgumentException),
    "A userId of null was inappropriately allowed.")]
public void NullUserIdInConstructor()
{
   LogonInfo logonInfo = new LogonInfo(null, "[email protected]");
}
 dbkk01 июн. 2009 г., 07:20
Приведенный выше атрибут ExpectedException также работает в NUnit (но [TestMethod] должен быть [Test]).
 Ruben Bartelink25 июн. 2009 г., 12:48
@dbkk: Не работает точно так же в NUnit - сообщение обрабатывается как строка, которая должна соответствовать сообщению об исключении (и IU считает, что это имеет больше смысла)
 steve01 авг. 2014 г., 17:31
Этот атрибут выполняет работу и является встроенной функцией для программистов на c #, но я не рекомендую использовать его, поскольку он недостаточно гибок. Подумайте, что произойдет, если ваш код установки теста выдает тип исключения: тест проходит успешно, но на самом деле не выполняет то, что вы ожидали. Или что, если вы хотите проверить состояние объекта исключения. Я обычно хочу использовать StringAssert.Contains (e.Message ...), а не тестировать все сообщение. Используйте метод assert, как описано в других ответах.
 Gopal14 сент. 2017 г., 13:24
Вы можете использовать Assert.ThrowsException <T> и Assert.ThrowsExceptionAsync <T> в MsTest.
 Terence17 мая 2016 г., 20:12
Избегайте использования ExpectedException в NUnit, так как он будет отброшен в NUnit 3.0. Я предпочитаю использовать Assert.Throws <SpecificException> ()

выписыватьсяnUnit Docs для примеров о:

[ExpectedException( typeof( ArgumentException ) )]

которую можно использовать так:

Assert.ThrowsException<System.FormatException>(() =>
            {
                Story actual = PersonalSite.Services.Content.ExtractHeader(String.Empty);
            }); 

Вы можете установить его с помощью nuget:Install-Package MSTest.TestFramework

 C.M.16 нояб. 2018 г., 19:17
В 2018 году это считается наилучшей практикой, поскольку проверяется только выбрасываемый тестируемый модуль, а не какой-либо другой код.

к:

[ExpectedException(typeof(ExceptionType))]
public void YourMethod_should_throw_exception()

предоставленный @Richiban выше, прекрасно работает, за исключением того, что он не обрабатывает ситуацию, когда выдается исключение, но не ожидаемый тип. Следующие адреса, которые:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace YourProject.Tests
{
    public static class MyAssert
    {
        /// <summary>
        /// Helper for Asserting that a function throws an exception of a particular type.
        /// </summary>
        public static void Throws<T>( Action func ) where T : Exception
        {
            Exception exceptionOther = null;
            var exceptionThrown = false;
            try
            {
                func.Invoke();
            }
            catch ( T )
            {
                exceptionThrown = true;
            }
            catch (Exception e) {
                exceptionOther = e;
            }

            if ( !exceptionThrown )
            {
                if (exceptionOther != null) {
                    throw new AssertFailedException(
                        String.Format("An exception of type {0} was expected, but not thrown. Instead, an exception of type {1} was thrown.", typeof(T), exceptionOther.GetType()),
                        exceptionOther
                        );
                }

                throw new AssertFailedException(
                    String.Format("An exception of type {0} was expected, but no exception was thrown.", typeof(T))
                    );
            }
        }
    }
}
 Tom Lint27 авг. 2014 г., 16:18
@Martin Я бы удалил код, включающий исключение-другое, и просто перебросил бы из второго предложения catch
 Crono29 нояб. 2013 г., 21:08
Хммм ... Я понимаю идею, но я не уверен, что согласен, что лучше. Тот факт, что мы хотим убедиться, что определенное исключение поднято, не означает, что все остальные должны быть рассмотрены как ошибка подтверждения. ИМХО, неизвестное исключение должно просто пузыриться в стеке, как в любой другой операции assert.

я подведу итог тому, что все здесь говорили раньше ... В любом случае, вот код, который я построил в соответствии с хорошими ответами :) Осталось только скопировать и использовать ...

/// <summary>
/// Checks to make sure that the input delegate throws a exception of type TException.
/// </summary>
/// <typeparam name="TException">The type of exception expected.</typeparam>
/// <param name="methodToExecute">The method to execute to generate the exception.</param>
public static void AssertRaises<TException>(Action methodToExecute) where TException : System.Exception
{
    try
    {
        methodToExecute();
    }
    catch (TException) {
        return;
    }  
    catch (System.Exception ex)
    {
        Assert.Fail("Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead.");
    }
    Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown.");  
}

Если ваша операцияfoo.bar() является асинхронным:

await Assert.ThrowsExceptionAsync<Exception>(() => foo.bar());

Еслиfoo.bar() не асинхронный

Assert.ThrowsException<Exception>(() => foo.bar());
 Chris Schaller04 февр. 2019 г., 04:07
Есть много других ответов, для меня я искал краткий способ проверки на известные условия сбоя только по типу исключения, это облегчает чтение тестовых случаев. ПРИМЕЧАНИЕ. Тип исключения не совпадает с унаследованными классами исключений, такими как стандартный try-catch, поэтому приведенный выше пример не будет перехватыватьArgumentException например. Старый Try Catch и проверка ответа на исключение по-прежнему предпочтительнее, если у вас есть расширенные критерии для тестирования, но для многих моих случаев это очень помогает!

Если вы используете MSTest, который изначально не имелExpectedException атрибут, вы могли бы сделать это:

try 
{
    SomeExceptionThrowingMethod()
    Assert.Fail("no exception thrown");
}
catch (Exception ex)
{
    Assert.IsTrue(ex is SpecificExceptionType);
}
 steve01 авг. 2014 г., 17:41
Это работает, но я не рекомендую это вообще, так как логика слишком сложна. Не сказать, что это запутанно, но подумайте, если вы напишите этот блок кода для нескольких тестов - 10, 100 тестов. Эта логика должна быть разработана для хорошо разработанного метода подтверждения. Смотрите другие ответы.

лишком ограничен и подвержен ошибкам) или записывать блок try / catch в каждом тесте (поскольку он слишком сложен и подвержен ошибкам). Используйте хорошо разработанный метод assert - либо предоставленный вашей тестовой средой, либо напишите свой собственный. Вот что я написал и использую.

public static class ExceptionAssert
{
    private static T GetException<T>(Action action, string message="") where T : Exception
    {
        try
        {
            action();
        }
        catch (T exception)
        {
            return exception;
        }
        throw new AssertFailedException("Expected exception " + typeof(T).FullName + ", but none was propagated.  " + message);
    }

    public static void Propagates<T>(Action action) where T : Exception
    {
        Propagates<T>(action, "");
    }

    public static void Propagates<T>(Action action, string message) where T : Exception
    {
        GetException<T>(action, message);
    }

    public static void Propagates<T>(Action action, Action<T> validation) where T : Exception
    {
        Propagates(action, validation, "");
    }

    public static void Propagates<T>(Action action, Action<T> validation, string message) where T : Exception
    {
        validation(GetException<T>(action, message));
    }
}

Пример использования:

    [TestMethod]
    public void Run_PropagatesWin32Exception_ForInvalidExeFile()
    {
        (test setup that might propagate Win32Exception)
        ExceptionAssert.Propagates<Win32Exception>(
            () => CommandExecutionUtil.Run(Assembly.GetExecutingAssembly().Location, new string[0]));
        (more asserts or something)
    }

    [TestMethod]
    public void Run_PropagatesFileNotFoundException_ForExecutableNotFound()
    {
        (test setup that might propagate FileNotFoundException)
        ExceptionAssert.Propagates<FileNotFoundException>(
            () => CommandExecutionUtil.Run("NotThere.exe", new string[0]),
            e => StringAssert.Contains(e.Message, "NotThere.exe"));
        (more asserts or something)
    }

ПРИМЕЧАНИЯ

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

В отличие от других, я использую «распространения» вместо «бросков», поскольку мы можем только проверить, распространяется ли исключение из вызова. Мы не можем напрямую проверить, что выброшено исключение. Но я полагаю, вы могли бы бросать изображения, чтобы означать: брошенный и не пойман.

ЗАКЛЮЧИТЕЛЬНАЯ МЫСЛЬ

Прежде чем перейти к такому подходу, я рассмотрел использование атрибута ExpectedException, когда тест проверял только тип исключения, и использование блока try / catch, если требовалась дополнительная проверка. Но я не только должен был думать о том, какую технику использовать для каждого теста, но и менять код с одной методики на другую по мере изменения потребностей не было тривиальной задачей. Использование одного последовательного подхода экономит умственные усилия.

Таким образом, в общем, этот подход спортивный: простота использования, гибкость и надежность (трудно сделать это неправильно).

я хотел бы добавить новую мысль к обсуждению. Я расширил шаблон «Упорядочить, Действовать, Утвердить», чтобы ожидать, «Организовать, Действовать, Утвердить» Вы можете сделать указатель ожидаемого исключения, а затем утверждать, что он был назначен. Это кажется более чистым, чем выполнение ваших утверждений в блоке catch, оставляя раздел Act в основном только для одной строки кода для вызова тестируемого метода. Вы также не должныAssert.Fail(); или жеreturn из нескольких точек в коде. Любое другое выброшенное исключение вызовет сбой теста, потому что он не будет перехвачен, и если будет сгенерировано исключение вашего ожидаемого типа, но это было не то, что вы ожидали, Утверждение против сообщения или других свойств исключение поможет убедиться, что ваш тест не пройдет непреднамеренно.

[TestMethod]
public void Bar_InvalidDependency_ThrowsInvalidOperationException()
{
    // Expectations
    InvalidOperationException expectedException = null;
    string expectedExceptionMessage = "Bar did something invalid.";

    // Arrange
    IDependency dependency = DependencyMocks.Create();
    Foo foo = new Foo(dependency);

    // Act
    try
    {
        foo.Bar();
    }
    catch (InvalidOperationException ex)
    {
        expectedException = ex;
    }

    // Assert
    Assert.IsNotNull(expectedException);
    Assert.AreEqual(expectedExceptionMessage, expectedException.Message);
}

над которым я работаю, у нас есть другое решение, которое делает это.

Во-первых, мне не нравится атрибут ExpectedExceptionAttribute, потому что он принимает во внимание, какой вызов метода вызвал исключение.

Я делаю это с помощью помощника.

Контрольная работа

[TestMethod]
public void AccountRepository_ThrowsExceptionIfFileisCorrupt()
{
     var file = File.Create("Accounts.bin");
     file.WriteByte(1);
     file.Close();

     IAccountRepository repo = new FileAccountRepository();
     TestHelpers.AssertThrows<SerializationException>(()=>repo.GetAll());            
}

HelperMethod

public static TException AssertThrows<TException>(Action action) where TException : Exception
    {
        try
        {
            action();
        }
        catch (TException ex)
        {
            return ex;
        }
        Assert.Fail("Expected exception was not thrown");

        return null;
    }

Аккуратно, не правда ли;)

лучший вариант, чемExpectedException атрибут должен использоватьShoudly«sShould.Throw.

Should.Throw<DivideByZeroException>(() => { MyDivideMethod(1, 0); });

Допустим, у нас есть требование, чтобыпокупатель должен иметьадрес создатьзаказ, Если нет, тоCreateOrderForCustomer метод должен привести кArgumentException, Тогда мы могли бы написать:

[TestMethod]
public void NullUserIdInConstructor()
{
  var customer = new Customer(name := "Justin", address := null};

  Should.Throw<ArgumentException>(() => {
    var order = CreateOrderForCustomer(customer) });
}

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

Обратите внимание, что есть такжеShould.ThrowAsync для тестирования асинхронного метода.

PM> Install-пакет MSTestExtensions это добавляетAssert.Throws () синтаксис в стиле nUnit / xUnit для MsTest.

Инструкции высокого уровня: загрузите сборку и наследуйте отBaseTest и вы можете использоватьAssert.Throws () синтаксис.

Основной метод для реализации Throws выглядит следующим образом:

public static void Throws<T>(Action task, string expectedMessage, ExceptionMessageCompareOptions options) where T : Exception
{
    try
    {
        task();
    }
    catch (Exception ex)
    {
        AssertExceptionType<T>(ex);
        AssertExceptionMessage(ex, expectedMessage, options);
        return;
    }

    if (typeof(T).Equals(new Exception().GetType()))
    {
        Assert.Fail("Expected exception but no exception was thrown.");
    }
    else
    {
        Assert.Fail(string.Format("Expected exception of type {0} but no exception was thrown.", typeof(T)));
    }
}

Раскрытие: я собрал этот пакет.

Больше информации:http://www.bradoncode.com/blog/2012/01/asserting-exceptions-in-mstest-with.html

 Lane Goolsby23 июн. 2015 г., 20:08
Спасибо за пример. У вас есть пример того, как проверить Assert.DoesNotThrow () или эквивалент?

так как это может привести к нескольким подводным камням, как показано здесь:

http://geekswithblogs.net/sdorman/archive/2009/01/17/unit-testing-and-expected-exceptions.aspx

И здесь:

http://xunit.github.io/docs/comparisons.html

Если вам нужно проверить исключения, есть и другие, которые не одобряются. Вы можете использовать метод try {act / fail} catch {assert}, который может быть полезен для платформ, которые не имеют прямой поддержки для тестов исключений, кроме ExpectedException.

Лучшей альтернативой является использование xUnit.NET, которая является очень современной, ориентированной на будущее и расширяемой структурой модульного тестирования, которая извлекла уроки из всех других ошибок и усовершенствована. Одним из таких улучшений является Assert.Throws, который обеспечивает гораздо лучший синтаксис для утверждения исключений.

Вы можете найти xUnit.NET на github:http://xunit.github.io/

 jrista06 апр. 2011 г., 18:50
@Ant: MS скопировала NUnit ... так что реальный вопрос в том, почему NUnit подумал, что это хорошая идея?
 Alconja01 июн. 2009 г., 07:54
Обратите внимание, что NUnit 2.5 также теперь поддерживает синтаксис стиля Assert.Throws -nunit.com/index.php?p=releaseNotes&r=2.5
 Ant06 апр. 2011 г., 14:25
То, что юнит-тесты останавливаются, чтобы вы знали об исключении при использовании ExpectedException, сводит меня с ума. Почему MS посчитал хорошей идеей сделать ручной шаг в автоматических тестах? Спасибо за ссылки.

s и использовать его, как и любой другой метод Assert. К сожалению, .NET не позволяет вам писать метод статического расширения, поэтому вы не можете использовать этот метод, как если бы он действительно принадлежал сборке в классе Assert; просто сделайте другой под названием MyAssert или что-то подобное. Класс выглядит так:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace YourProject.Tests
{
    public static class MyAssert
    {
        public static void Throws<T>( Action func ) where T : Exception
        {
            var exceptionThrown = false;
            try
            {
                func.Invoke();
            }
            catch ( T )
            {
                exceptionThrown = true;
            }

            if ( !exceptionThrown )
            {
                throw new AssertFailedException(
                    String.Format("An exception of type {0} was expected, but not thrown", typeof(T))
                    );
            }
        }
    }
}

Это означает, что ваш юнит-тест выглядит так:

[TestMethod()]
public void ExceptionTest()
{
    String testStr = null;
    MyAssert.Throws<NullReferenceException>(() => testStr.ToUpper());
}

Который выглядит и ведет себя намного больше, чем остальные ваши синтаксисы модульных тестов.

 g t18 янв. 2013 г., 10:10
Избавьтесь от флага bool и поместите бросок на строку сразу после вызова для более компактной реализации.
 David Sherret07 сент. 2014 г., 06:21
Спасибо! Это кажется лучшим подходом для меня, потому что это короткий способ проверить несколько исключений в одном методе. Это также намного более читабельно.
 Niall Connaughton04 мая 2015 г., 08:57
@gt - см. другие комментарии в этой теме. Если T - Исключение, проверка, чтобы гарантировать, что исключение было брошено, не будет работать.
 freedomn-m06 июн. 2016 г., 16:16
Атрибуты @MickeyPerlstein нарушают правила AAA для тестирования. В частности, если ваше Соглашение сгенерировало исключение еще до того, как вы дойдете до Акта, тогда ваш тест пройден ... еек!
 Mark Hildreth01 нояб. 2013 г., 22:16
Единственное, что делает это лучше, это то, что функция возвращает пойманное исключение, чтобы вы могли продолжать утверждать, что такие вещи, как атрибуты исключения, являются правильными.

и он недостаточно гибок, вы всегда можете сделать это:

try {
    somethingThatShouldThrowAnException();
    Assert.Fail(); // If it gets to this line, no exception was thrown
} catch (GoodException) { }

Как указывает @Jonas, это НЕ работает для отлова базового исключения:

try {
    somethingThatShouldThrowAnException();
    Assert.Fail(); // raises AssertionException
} catch (Exception) {
    // Catches the assertion exception, and the test passes
}

Если вам абсолютно необходимо перехватить Exception, вам нужно сбросить Assert.Fail (). Но на самом деле, это признак того, что вы не должны писать это от руки; проверьте в своей тестовой среде параметры или посмотрите, можете ли вы выдать более значимое исключение для проверки.

catch (AssertionException) { throw; }

Вы должны быть в состоянии адаптировать этот подход к тому, что вам нравится, включая указание, какие исключения нужно ловить. Если вы ожидаете только определенные типы, закончитеcatch блокируется с помощью:

} catch (GoodException) {
} catch (Exception) {
    // not the right kind of exception
    Assert.Fail();
}
 Pavel Repin01 июн. 2009 г., 07:38
+1, я использую этот способ вместо атрибута, когда мне нужно сделать утверждения, выходящие за рамки только типа исключения. Например, что делать, если нужно проверить, что для определенных полей в экземпляре исключения установлены определенные значения.
 Jonas06 мар. 2013 г., 11:45
@ Vinnyq12 Я имею в виду, что первый тест в приведенном выше примере никогда не будет неудачным. Сбой теста, если выброшено исключение (а не «catch» ExpectedExceptionAttribute)
 evilfish07 янв. 2013 г., 13:16
Я думаю, что это решение является лучшим. [ExpectedException (typeof (ArgumentException))] имеет свои применения, если тест прост, но, на мой взгляд, это ленивое решение, и его неудобство может привести к подводным камням. Это решение дает вам конкретный контроль для проведения более правильного теста, а также вы можете сделать тест Writeline в своем отчете о выполнении теста, чтобы исключение действительно было выдано, как и ожидалось.
 Jonas15 февр. 2013 г., 11:54
Будьте осторожны с этим, потому что Assert.Fail () вызывает исключение, если вы его ловите, проход теста!
 mibollma20 июл. 2012 г., 10:05
Вы не обязаны указывать сообщение об ошибке. Этого достаточно: [ExpectedException (typeof (ArgumentException))]

Ваш ответ на вопрос