Как использовать Assert для проверки того, что было создано исключение?
Как использовать Assert (или другой тестовый класс?), чтобы убедиться, что исключение было брошено?
20 ответов:
для "Visual Studio 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, "P@ss0word"); }
обычно ваша структура тестирования будет иметь ответ на это. Но если он недостаточно гибкий, вы всегда можете сделать это:
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 }
если вы абсолютно должны поймать исключение, вы должны повторно утверждают.Неудача.)( Но на самом деле это знак, который вы не должны писать от руки; проверьте свою тестовую структуру на наличие опций или посмотрите, можете ли вы создать более значимое исключение для тестирования для.
catch (AssertionException) { throw; }
вы должны быть в состоянии адаптировать этот подход к тому, что вам нравится-в том числе указав, какие виды исключений поймать. Если вы ожидаете только определенные типы, закончить
catch
блоки с:} catch (GoodException) { } catch (Exception) { // not the right kind of exception Assert.Fail(); }
мой предпочтительный метод для реализации этого-написать метод, называемый бросками, и использовать его так же, как и любой другой метод 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()); }
, который выглядит и ведет себя гораздо больше, как и остальные синтаксисы модульного теста.
Если вы используете NUNIT, вы можете сделать что-то вроде этого:
Assert.Throws<ExpectedException>(() => methodToTest());
Также можно сохранить брошенное исключение для его дальнейшей проверки:ExpectedException ex = Assert.Throws<ExpectedException>(() => methodToTest()); Assert.AreEqual( "Expected message text.", ex.Message ); Assert.AreEqual( 5, ex.SomeNumber);
будьте осторожны с использованием 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.Бросает, что обеспечивает гораздо лучший синтаксис для утверждения исключений.
вы можете найти 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<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; }
аккуратно, не так;)
это атрибут метода тестирования... вы не используете Assert. Выглядит так:
[ExpectedException(typeof(ExceptionType))] public void YourMethod_should_throw_exception()
вы можете скачать пакет из NuGet с помощью: PM> Install-Package MSTestExtensions добавляет утверждать.Броски () синтаксис в стиле nUnit / xUnit для MsTest.
инструкции высокого уровня: загрузите сборку и унаследуйте от BaseTest и вы можете использовать утверждать.Броски () синтаксис.
основной метод реализации бросков выглядит следующим образом:
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
MSTest теперь имеет Assert.ThrowsException функция, которая может быть использована следующим образом:
Assert.ThrowsException<System.FormatException>(() => { Story actual = PersonalSite.Services.Content.ExtractHeader(String.Empty); });
Я не рекомендую использовать атрибут ExpectedException (поскольку он слишком ограничен и подвержен ошибкам) или писать блок 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) }
Примечания
возврат исключения вместо поддержки обратного вызова проверки является разумная идея, за исключением того, что это делает синтаксис вызова этого утверждения очень отличным от других утверждений, которые я использую.
В отличие от других, я использую 'propagates' вместо 'throws', так как мы можем только проверить, распространяется ли исключение из вызова. Мы не можем проверить напрямую, что выбрасывается исключение. Но я полагаю, что вы могли бы изобразить броски, чтобы означать: брошенный и не пойманный.
ПОСЛЕДНЯЯ МЫСЛЬ
перед переключением на такой подход я рассмотрел использование Атрибут ExpectedException, когда тест проверяет только тип исключения и использует блок try/catch, если требуется дополнительная проверка. Но, мало того, что я должен был бы думать о том, какой метод использовать для каждого теста, но изменение кода от одного метода к другому по мере изменения потребностей не было тривиальным усилием. Использование одного последовательного подхода экономит умственные усилия.
помощник, предоставленный @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)) ); } } } }
Ну, я в значительной степени суммирую то, что все остальные здесь говорили раньше...В любом случае, вот код, который я построил в соответствии с хорошими ответами :) все, что осталось сделать, это скопировать и использовать...
/// <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."); }
поскольку вы упоминаете использование других тестовых классов, лучший вариант, чем использовать Shoudly ' s должны.Брось.
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) }); }
это лучше, чем использовать потому что мы конкретизируем, что должно выбросить ошибку. Это делает требования в наших тестах более четкими, а также облегчает диагностику, когда тест не проходит.
обратите внимание, что есть также
Should.ThrowAsync
для асинхронного метода тестирования.
проверить nUnit Docs примеры:
[ExpectedException( typeof( ArgumentException ) )]
Это будет зависеть от того, какой тестовый фреймворк вы используете?
в MbUnit, например, вы можете указать ожидаемое исключение с атрибутом, чтобы убедиться, что вы получаете исключение, вы действительно ожидаете.
[ExpectedException(typeof(ArgumentException))]
в качестве альтернативы вы можете попробовать тестирование исключения на самом деле выбрасываются со следующими 2 строками в тесте.
var testDelegate = () => MyService.Method(params); Assert.Throws<Exception>(testDelegate);
при использовании Нанит, попробуйте это:
Assert.That(() => { Your_Method_To_Test(); }, Throws.TypeOf<Your_Specific_Exception>().With.Message.EqualTo("Your_Specific_Message"));
в VS встроенное модульное тестирование если вы просто хотите проверить, что" любое исключение " брошено, но вы не знаете тип, вы можете использовать catch all:
[TestMethod] [ExpectedException(typeof(Exception), AllowDerivedTypes = true)] public void ThrowExceptionTest() { //... }
хотя это старый вопрос, я хотел бы добавить новую мысль для обсуждения. Я расширил шаблон аранжировки, действия, утверждения, который следует ожидать, аранжировать, действовать, утверждать. Вы можете сделать ожидаемый указатель исключения, а затем утверждать, что он был назначен. Это кажется более чистым, чем делать ваши утверждения в блоке 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); }