C# 使用Assert测试异常以确保抛出异常的最佳方法
您认为这是测试异常的好方法吗?有什么建议吗C# 使用Assert测试异常以确保抛出异常的最佳方法,c#,.net,unit-testing,C#,.net,Unit Testing,您认为这是测试异常的好方法吗?有什么建议吗 Exception exception = null; try{ //I m sure that an exeption will happen here } catch (Exception ex){ exception = ex; } Assert.IsNotNull(exception); 我正在使用MS Test。使用ExpectedException属性标记测试(这是NUnit或MSTest中的术语;其他单元测试框架的用户
Exception exception = null;
try{
//I m sure that an exeption will happen here
}
catch (Exception ex){
exception = ex;
}
Assert.IsNotNull(exception);
我正在使用MS Test。使用ExpectedException属性标记测试(这是NUnit或MSTest中的术语;其他单元测试框架的用户可能需要翻译)。对于大多数.net单元测试框架,您可以在测试方法上添加[ExpectedException]属性。但是,这不能告诉您异常发生在您预期的时间点。那是我能帮忙的地方 使用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));
}
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);
[事实]
public void CantDecrementBasketLineQuantityBelowZero()以下
{
var o=新篮子();
var p=新产品{Id=1,净价格=2345万};
o、 AddProduct(p,1);
抛出(()=>o.SetProductQuantity(p,-3));
}
[事实]是[TestMethod]的xunit等价物。我使用了两种不同的模式。在预期出现异常的大多数情况下,我都使用
ExpectedException
属性。这在大多数情况下是足够的,但是,在某些情况下这是不够的。异常可能不可捕获—因为它是由反射调用的方法引发的—或者我只是想检查其他条件是否成立,比如回滚了事务或者仍然设置了某个值。在这些情况下,我将其包装在一个try/catch
块中,该块期望出现准确的异常,如果代码成功,则执行Assert.Fail
,并捕获一般异常,以确保不会引发其他异常
第一种情况:
[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 )
);
}
}
作为使用
ExpectedException
属性的替代方法,我有时会为测试类定义两种有用的方法:
断言throwsexception()
接受一个委托,并断言它会抛出预期的异常和预期的消息
AssertDoesNotThrowException()
接受同一委托并断言它不会引发异常
当您想测试在一种情况下是否引发异常,但在另一种情况下却没有引发异常时,此配对非常有用
使用它们,我的单元测试代码可能如下所示:
[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));
}
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);
又漂亮又整洁,嗯
MyAssertThrowException()
和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, e.g. 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"/>. (e.g. 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);
}
}
protected delegate void exception rower();
///
///断言调用方法会导致声明类型的异常以及声明的消息。
///
///调用要测试的方法的委托。
///异常的预期类型,例如typeof(FormatException)。
///预期的异常消息(或整个消息的片段)
受保护的无效资产ThrowException(ExceptionRower ExceptionRowingFunc,类型expectedExceptionType,字符串expectedExceptionMessage)
{
尝试
{
例外RowingFunc();
Assert.Fail(“调用未引发任何异常,但应引发一个异常。”);
}
catch(NUnit.Framework.AssertionException)
{
//忽略并重新显示NUnit异常
投掷;
}
捕获(例外情况除外)
{
IsInstanceOfType(expectedExceptionType,例如,“引发的异常不是预期的类型”);
Assert.IsTrue(例如Message.Contains(expectedExceptionMessage),“引发的异常不包含预期的消息。预期=\”“+expectedExceptionMessage+”\,获取\”“+ex.Message+”\);
}
}
///
///断言调用方法不会引发异常。
///
///
///这通常仅与一起使用。(例如,一旦您测试了一个例外Rower
///方法引发异常,然后您的测试可能会修复异常的原因,然后调用此方法以确保它现在已修复)。
///
///调用要测试的方法的委托。
受保护的无效资产不存在异常(例外Rower例外RowingFunc)
{
尝试
{
例外RowingFunc();
}
catch(NUnit.Framework.AssertionException)
{
//忽略并重新显示任何NUnit异常
投掷;
}
捕获(例外情况除外)
{
Assert.Fail(“调用引发意外异常:“+ex.Message”);
}
}
我是新来的,没有评论或否决的名声,但我想指出以下示例中的一个缺陷:
在我熟悉的所有单元测试框架中,Assert.Fail
通过抛出异常来工作,因此泛型捕获实际上会掩盖测试失败。如果somethingthausesanexception()
没有抛出,则将抛出Assert.Fail
,但这不会向测试运行程序冒泡以指示失败
如果您需要捕获预期的异常(即,断言某些细节,如异常上的消息/属性),那么捕获特定的预期类型而不是基本异常类很重要。这将允许出现Assert.Fail
异常(假设您没有抛出与单元测试框架相同类型的异常),但仍然允许验证由somethingthausesanception()引发的异常
方法。自v起,对于测试异常,具有以下方法级别Assert
s:
,将测试确切的异常类型:
Assert.Throws<NullReferenceException>(() => someNullObject.ToString());
另外,在调试抛出异常的单元测试时,您可能希望防止VS
编辑
下面仅举一个Matthew评论的例子,泛型Assert.Throws
和Assert.Catch
的返回是异常类型的异常,您可以对其进行进一步检查:
// The type of ex is that of the generic type parameter (SqlException)
var ex = Assert.Throws<SqlException>(() => MethodWhichDeadlocks());
Assert.AreEqual(1205, ex.Number);
//ex的类型是泛型类型参数(SqlException)的类型
var ex=Assert.Throws(()=>methodwhichheadlocks());
断言.AreEqual(1205,例如。
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));
Assert.ThrowsException<Exception>(() => myClass.MyMethodWithError());
//async version
await Assert.ThrowsExceptionAsync<SomeException>(
() => myObject.SomeMethodAsync()
);
[Test]
[TestCase(null)]
public void FooCalculation_InvalidInput_ShouldThrowArgumentNullExeption(string text)
{
var foo = new Foo();
Assert.That(() => foo.Calculate(text), Throws.ArgumentNullExeption);
//Or:
Assert.That(() => foo.Calculate(text), Throws.Exception.TypeOf<ArgumentNullExeption>);
}
var result = await Assert.ThrowsExceptionAsync<HttpRequestException>(async ()=>
{
await myService.SomeMethodAsync("test value");
}
Assert.AreEqual("Response status code does not indicate success: 401 (Unauthorized).", result);