用Asserttestingexception的最好方法是确保它们被抛出

你认为这是testingexception的好方法吗? 有什么build议么?

Exception exception = null; try{ //I m sure that an exeption will happen here } catch (Exception ex){ exception = ex; } Assert.IsNotNull(exception); 

我正在使用MS Test。

我有几个不同的模式,我使用。 大多数情况下,预期exception时我使用ExpectedException属性。 这在大多数情况下是足够的,然而,有些情况下这是不够的。 exception可能不可捕捉 – 因为它是由reflection调用的方法抛出的,或者我只是想检查其他条件是否成立,比如说事务被回滚或者某个值已经被设置。 在这些情况下,我将它封装在一个try/catch块中,这个块需要确切的exception,如果代码成功,就执行一个Assert.Fail ,并捕获一般的exception,以确保不会抛出一个不同的exception。

第一种情况:

 [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void MethodTest() { var obj = new ClassRequiringNonNullParameter( null ); } 

第二种情况:

 [TestMethod] public void MethodTest() { try { var obj = new ClassRequiringNonNullParameter( null ); Assert.Fail("An exception should have been thrown"); } catch (ArgumentNullException ae) { Assert.AreEqual( "Parameter cannot be null or empty.", ae.Message ); } catch (Exception e) { Assert.Fail( string.Format( "Unexpected exception of type {0} caught: {1}", e.GetType(), e.Message ) ); } } 

我在这里是新来的,没有评论或者低估的声望,但是想要指出安迪·怀特的回答中的一个缺陷:

 try { SomethingThatCausesAnException(); Assert.Fail("Should have exceptioned above!"); } catch (Exception ex) { // whatever logging code } 

在我熟悉的所有unit testing框架中, Assert.Fail通过抛出exception来工作,所以genericscatch实际上会掩盖testing的失败。 如果SomethingThatCausesAnException()不抛出, Assert.Fail将会,但是永远不会冒泡到testing运行器来指示失败。

如果您需要捕获预期的exception(即断言某些细节,如exception的消息/属性),捕获特定的期望types非常重要,而不是捕获基本的Exception类。 这将允许Assert.Failexception冒出(假设你没有抛出与你的unit testing框架相同的exceptiontypes),但是仍然允许对SomethingThatCausesAnException()方法引发的exception进行validation。

从v 2.5开始,NUnit具有以下用于testingexception的方法级别的Assert

Assert.Throws ,它将testing一个确切的exceptiontypes:

 Assert.Throws<NullReferenceException>(() => someNullObject.ToString()); 

Assert.Catch将testing给定types的exception,或从此类派生的exceptiontypes:

 Assert.Catch<Exception>(() => someNullObject.ToString()); 

另外,当debugging抛出exception的unit testing时,你可能想阻止VS 打破exception 。

编辑

举个例子,下面是Matthew的评论,通用Assert.ThrowsAssert.Catch的返回是exceptiontypes的exception,然后您可以检查以进一步检查:

 // The type of ex is that of the generic type parameter (SqlException) var ex = Assert.Throws<SqlException>(() => MethodWhichDeadlocks()); Assert.AreEqual(1205, ex.Number); 

作为使用ExpectedException属性的替代方法,我有时会为我的testing类定义两个有用的方法:

AssertThrowsException()需要一个委托,并声明它将引发预期的exception与预期的消息。

AssertDoesNotThrowException()采用相同的委托并断言它不会引发exception。

当你想testing在一种情况下引发exception,而不是另一种情况时,这种配对可能非常有用。

使用他们我的unit testing代码可能是这样的:

 ExceptionThrower callStartOp = delegate(){ testObj.StartOperation(); }; // Check exception is thrown correctly... AssertThrowsException(callStartOp, typeof(InvalidOperationException), "StartOperation() called when not ready."); testObj.Ready = true; // Check exception is now not thrown... AssertDoesNotThrowException(callStartOp); 

又好又整洁吧?

AssertThrowsException()AssertDoesNotThrowException()方法在一个公共基类上定义如下:

 protected delegate void ExceptionThrower(); /// <summary> /// Asserts that calling a method results in an exception of the stated type with the stated message. /// </summary> /// <param name="exceptionThrowingFunc">Delegate that calls the method to be tested.</param> /// <param name="expectedExceptionType">The expected type of the exception, eg typeof(FormatException).</param> /// <param name="expectedExceptionMessage">The expected exception message (or fragment of the whole message)</param> protected void AssertThrowsException(ExceptionThrower exceptionThrowingFunc, Type expectedExceptionType, string expectedExceptionMessage) { try { exceptionThrowingFunc(); Assert.Fail("Call did not raise any exception, but one was expected."); } catch (NUnit.Framework.AssertionException) { // Ignore and rethrow NUnit exception throw; } catch (Exception ex) { Assert.IsInstanceOfType(expectedExceptionType, ex, "Exception raised was not the expected type."); Assert.IsTrue(ex.Message.Contains(expectedExceptionMessage), "Exception raised did not contain expected message. Expected=\"" + expectedExceptionMessage + "\", got \"" + ex.Message + "\""); } } /// <summary> /// Asserts that calling a method does not throw an exception. /// </summary> /// <remarks> /// This is typically only used in conjunction with <see cref="AssertThrowsException"/>. (eg once you have tested that an ExceptionThrower /// method throws an exception then your test may fix the cause of the exception and then call this to make sure it is now fixed). /// </remarks> /// <param name="exceptionThrowingFunc">Delegate that calls the method to be tested.</param> protected void AssertDoesNotThrowException(ExceptionThrower exceptionThrowingFunc) { try { exceptionThrowingFunc(); } catch (NUnit.Framework.AssertionException) { // Ignore and rethrow any NUnit exception throw; } catch (Exception ex) { Assert.Fail("Call raised an unexpected exception: " + ex.Message); } } 

不幸的是,MSTest STILL只有ExpectedException属性(只显示了MS有多less关心MSTest),IMO是非常糟糕的,因为它破坏了Arrange / Act / Assert模式,并且不允许你指定哪一行代码发生。

当我使用(/强制客户端)使用MSTest我总是使用这个帮手类:

 public static class AssertException { public static void Throws<TException>(Action action) where TException : Exception { try { action(); } catch (Exception ex) { Assert.IsTrue(ex.GetType() == typeof(TException), "Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead."); return; } Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown."); } public static void Throws<TException>(Action action, string expectedMessage) where TException : Exception { try { action(); } catch (Exception ex) { Assert.IsTrue(ex.GetType() == typeof(TException), "Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead."); Assert.AreEqual(expectedMessage, ex.Message, "Expected exception with a message of '" + expectedMessage + "' but exception with message of '" + ex.Message + "' was thrown instead."); return; } Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown."); } } 

使用示例:

 AssertException.Throws<ArgumentNullException>(() => classUnderTest.GetCustomer(null)); 

使用ExpectedExceptionAttribute标记testing(这是NUnit或MSTest中的术语;其他unit testing框架的用户可能需要翻译)。

现在,2017年,您可以使用新的MSTest V2框架轻松完成任务 :

 Assert.ThrowsException<Exception>(() => myClass.MyMethodWithError()); 

对于大多数.netunit testing框架,您可以在testing方法上放置一个[ExpectedException]属性。 然而,这不能告诉你,exception发生在你预期的地步。 这就是xunit.net可以提供帮助的地方。

用xunit你有Assert.Throws,所以你可以做这样的事情:

  [Fact] public void CantDecrementBasketLineQuantityBelowZero() { var o = new Basket(); var p = new Product {Id = 1, NetPrice = 23.45m}; o.AddProduct(p, 1); Assert.Throws<BusinessException>(() => o.SetProductQuantity(p, -3)); } 

[Fact]是[TestMethod]的xunit等价物,