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

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

    Для «Visual Team Team Test», похоже, вы применяете атрибут 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]"); } 

    Обычно для этой проверки у вас будет ответ. Но если это недостаточно гибко, вы всегда можете это сделать:

     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 } 

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

     catch (AssertionException) { throw; } 

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

     } catch (GoodException) { } catch (Exception) { // not the right kind of exception Assert.Fail(); } 

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

     using System; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace YourProject.Tests { public static class MyAssert { public static void Throws( 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(() => testStr.ToUpper()); } 

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

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

     try { SomeExceptionThrowingMethod() Assert.Fail("no exception thrown"); } catch (Exception ex) { Assert.IsTrue(ex is SpecificExceptionType); } 

    если вы используете NUNIT, вы можете сделать что-то вроде этого:

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

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

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

    См .: http://nunit.org/docs/2.5/exceptionAsserts.html.

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

    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/

    В проекте, над которым я работаю, есть еще одно решение.

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

    Я делаю это с помощью helpermethod.

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

     [TestMethod] public void AccountRepository_ThrowsExceptionIfFileisCorrupt() { var file = File.Create("Accounts.bin"); file.WriteByte(1); file.Close(); IAccountRepository repo = new FileAccountRepository(); TestHelpers.AssertThrows(()=>repo.GetAll()); } 

    HelperMethod

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

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

    Это атрибут метода тестирования … вы не используете Assert. Выглядит так:

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

    Вы можете загрузить пакет из Nuget, используя: PM> Install-Package MSTestExtensions, который добавляет синтаксис Assert.Throws () в стиле nUnit / xUnit для MsTest.

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

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

     public static void Throws(Action task, string expectedMessage, ExceptionMessageCompareOptions options) where T : Exception { try { task(); } catch (Exception ex) { AssertExceptionType(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

    MSTest теперь имеет функцию Assert.ThrowsException, которая может быть использована следующим образом:

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

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

     public static class ExceptionAssert { private static T GetException(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(Action action) where T : Exception { Propagates(action, ""); } public static void Propagates(Action action, string message) where T : Exception { GetException(action, message); } public static void Propagates(Action action, Action validation) where T : Exception { Propagates(action, validation, ""); } public static void Propagates(Action action, Action validation, string message) where T : Exception { validation(GetException(action, message)); } } 

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

      [TestMethod] public void Run_PropagatesWin32Exception_ForInvalidExeFile() { (test setup that might propagate Win32Exception) ExceptionAssert.Propagates( () => 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( () => CommandExecutionUtil.Run("NotThere.exe", new string[0]), e => StringAssert.Contains(e.Message, "NotThere.exe")); (more asserts or something) } 

    ЗАМЕТКИ

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

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

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

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

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

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

     using System; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace YourProject.Tests { public static class MyAssert { ///  /// Helper for Asserting that a function throws an exception of a particular type. ///  public static void Throws( 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)) ); } } } } 

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

     ///  /// Checks to make sure that the input delegate throws a exception of type TException. ///  /// The type of exception expected. /// The method to execute to generate the exception. public static void AssertRaises(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."); } 

    Поскольку вы упоминаете использование других тестовых classов, лучшим вариантом, чем атрибут ExpectedException является использование Shoudly ‘s Should.Throw .

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

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

     [TestMethod] public void NullUserIdInConstructor() { var customer = new Customer(name := "Justin", address := null}; Should.Throw(() => { var order = CreateOrderForCustomer(customer) }); } 

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

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

    Ознакомьтесь с инструкциями nUnit Docs :

     [ExpectedException( typeof( ArgumentException ) )] 

    Это зависит от того, какие frameworks тестирования вы используете?

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

     [ExpectedException(typeof(ArgumentException))] 

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

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

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

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

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

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

    Хотя это старый вопрос, я хотел бы добавить новую мысль к обсуждению. Я продлил шаблон Arrange, Act, Assert, который должен быть ожидаемым, упорядочить, действовать, утверждать. Вы можете сделать ожидаемый указатель исключения, а затем утверждать, что ему присвоен. Это кажется более чистым, чем выполнение ваших утверждений в блоке 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); } 
    Давайте будем гением компьютера.