如何使用Assert来validation是否抛出exception?
如何使用Assert(或其他Test类?)来validation是否抛出exception?
对于“Visual Studio团队testing”,看起来您将ExpectedException属性应用于testing的方法。
示例从这里的文档: unit testing演练与Visual Studio团队testing
[TestMethod] [ExpectedException(typeof(ArgumentException), "A userId of null was inappropriately allowed.")] public void NullUserIdInConstructor() { LogonInfo logonInfo = new LogonInfo(null, "P@ss0word"); }
通常你的testing框架将有一个答案。 但是,如果它不够灵活,你可以一直这样做:
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()。 但是,真的,这是一个标志,你不应该手写这个; 检查你的testing框架的选项,或看看你是否可以抛出一个更有意义的exception来testing。
catch (AssertionException) { throw; }
你应该能够适应这种方法,只要你喜欢 – 包括指定什么样的例外。 如果您只希望某些types,请使用以下命令完成catch
块:
} catch (GoodException) { } catch (Exception) { // not the right kind of exception Assert.Fail(); }
我的首选方法是编写一个名为Throws的方法,并像使用其他Assert方法一样使用它。 不幸的是,.NET不允许你编写一个静态的扩展方法,所以你不能使用这个方法,就好像它实际上属于Assert类中的构build; 只是做另一个叫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)) ); } } } }
这意味着你的unit testing看起来像这样:
[TestMethod()] public void ExceptionTest() { String testStr = null; MyAssert.Throws<NullReferenceException>(() => testStr.ToUpper()); }
看起来和行为更像你的unit testing语法的其余部分。
如果您正在使用最初没有ExpectedException
属性的MSTest,则可以这样做:
try { SomeExceptionThrowingMethod() Assert.Fail("no exception thrown"); } catch (Exception ex) { Assert.IsTrue(ex is SpecificExceptionType); }
谨慎使用ExpectedException,因为这可能导致几个陷阱,如下所示:
http://geekswithblogs.net/sdorman/archive/2009/01/17/unit-testing-and-expected-exceptions.aspx
和这里:
http://xunit.github.io/docs/comparisons.html
如果你需要testing例外情况,那么就不会有什么不好的地方。 您可以使用try {act / fail} catch {assert}方法,这对于除了ExpectedException之外的exceptiontesting没有直接支持的框架可能是有用的。
更好的select是使用xUnit.NET,这是一个非常现代的,前瞻性的,可扩展的unit testing框架,从其他所有错误中学习并改进。 Assert.Throws就是这样的一个改进,它为断言exception提供了更好的语法。
你可以在github上findxUnit.NET:http://xunit.github.io/
在我正在进行的一个项目中,我们有另外一个解决scheme。
首先,我不喜欢ExpectedExceptionAttribute,因为它考虑了导致exception的那个方法调用。
我用helper方法代替。
testing
[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; }
整洁,不是的;)
如果你使用NUNIT,你可以这样做:
Assert.Throws<ExpectedException>(() => methodToTest());
也可以存储抛出的exception,以便进一步validation它:
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
这是testing方法的一个属性…你不使用Assert。 看起来像这样:
[ExpectedException(typeof(ExceptionType))] public void YourMethod_should_throw_exception()
您可以使用以下命令从“Nuget”下载软件包: PM> Install-Package MSTestExtensions ,以nUnit / xUnit样式将Assert.Throws()语法添加到MsTest。
高级指令:下载程序集并从BaseTestinheritance,并且可以使用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
我不推荐使用ExpectedException属性(因为它太约束和容易出错),或者在每个testing中写一个try / catch块(因为它太复杂和容易出错)。 使用devise良好的断言方法 – 无论是由您的testing框架提供还是自己编写。 这是我写的和使用的。
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) }
笔记
返回exception而不是支持validationcallback是一个合理的想法,除非这样做使得这个断言的调用语法与我使用的其他断言非常不同。
与其他人不同,我使用“传播”而不是“抛出”,因为我们只能testingexception是否从调用中传播。 我们不能直接testing抛出exception。 但是我想你可以把这个意思形象地表示为:抛出而不是被抓住。
最后的想法
在切换到这种方法之前,我考虑在testing仅validationexceptiontypes时使用ExpectedException属性,如果需要更多validation,则使用try / catch块。 但是,我不仅要考虑每种testing使用哪种技术,而且需要改变的代码从一种技术转换到另一种技术并不是一件容易的事情。 使用一个一致的方法可以节省精力。
总而言之,这种方法体现了:易用性,灵活性和健壮性(很难做到这一点)。
上面@Richiban提供的帮助程序很好用,除非它不处理引发exception的情况,而不是预期的types。 以下地址是:
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)) ); } } } }
好吧,我几乎总结了其他人在这里之前所说的…不pipe怎么说,这是我根据好的答案build立的代码:)所有剩下要做的就是复制和使用…
/// <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."); }
这将取决于你使用的testing框架?
例如,在MbUnit中,您可以使用一个属性指定预期的exception,以确保您获得了您真正期望的exception。
[ExpectedException(typeof(ArgumentException))]
既然你提到使用其他testing类,比ExpectedException
属性更好的select是使用Shoudly的Should.Throw 。
Should.Throw<DivideByZeroException>(() => { MyDivideMethod(1, 0); });
比方说,我们有一个要求, 客户必须有一个地址来创build一个订单 。 否则, CreateOrderForCustomer
方法应该导致一个ArgumentException
CreateOrderForCustomer
。 那我们可以这样写:
[TestMethod] public void NullUserIdInConstructor() { var customer = new Customer(name := "Justin", address := null}; Should.Throw<ArgumentException>(() => { var order = CreateOrderForCustomer(customer) }); }
这比使用ExpectedException
属性要好,因为我们正在具体说明应该抛出什么错误。 这使我们的testing中的要求更加清晰,并且在testing失败时也使得诊断更容易。
请注意,还有一个用于asynchronous方法testing的Should.ThrowAsync
。
MSTest现在有一个Assert.ThrowsException函数,可以这样使用:
Assert.ThrowsException<System.FormatException>(() => { Story actual = PersonalSite.Services.Content.ExtractHeader(String.Empty); });
查看nUnit Docs,获取有关以下内容的示例:
[ExpectedException( typeof( ArgumentException ) )]
在使用NUnit的情况下,试试这个:
Assert.That(() => { Your_Method_To_Test(); }, Throws.TypeOf<Your_Specific_Exception>().With.Message.EqualTo("Your_Specific_Message"));
尽pipe这是一个老问题,但我想在讨论中增加新的思想。 我已经把“安排”,“行为”,“断言”模式扩展到预期,安排,行动,断言。 你可以做一个预期的exception指针,然后声明它被分配给。 这比在catch块中执行Asserts更清洁,让Act部分主要是为了一行代码来调用被testing的方法。 你也不必Assert.Fail();
或者从代码中的多个点return
。 抛出的任何其他exception都将导致testing失败,因为它不会被捕获,并且如果抛出了期望types的exception,但是它不是您期望的types,请根据消息或其他属性例外有助于确保您的testing不会无意中通过。
[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); }
作为替代scheme,您可以尝试testingexception事实上是在您的testing中随后的两行引发的。
var testDelegate = () => MyService.Method(params); Assert.Throws<Exception>(testDelegate);
在VS内置unit testing中,如果你只是想validation“任何exception”是否被抛出,但你不知道types,你可以使用catch全部:
[TestMethod] [ExpectedException(typeof(Exception), AllowDerivedTypes = true)] public void ThrowExceptionTest() { //... }