内部异常的单元测试
Unit testing for inner exceptions
我正在使用 Visual Studio 的集成框架编写一些单元测试。我需要编写一些测试用例,这些用例在抛出适当的异常时通过。问题是我需要测试的异常是嵌套在更一般的异常中的内部异常。是否有一些简单的解决方案,或者我是否需要扩展整个功能。我目前正在使用 [ExpectedException] 属性,但在这种情况下它不会有多大用处。
我也很好奇当我们使用 [ExpectedException] 时会发生什么,同时我们在测试本身中也有一些 Assert 逻辑。条件是否被评估(抛出异常并且 Assert 语句被证明是有效的)或测试在抛出正确的异常后立即通过?
不是一个完整的解决方案,但在 NUnit 中,您可以做这样的事情:
var ex = Assert.Throws<Exception>(() => thing.ThatThrows());
Assert.That(ex.InnerException, Is.TypeOf<BadException>() );
也许你可以在你的测试框架中?
如果你的框架不支持自定义抛出,你通常有两种选择:
- 自己实现
- 更改(或扩展)框架
我将从第二种解决方案开始。考虑使用 FluentAssertions 库。它允许你做这样的事情:
Action deleteUser = () => usersRepository.Delete(new User { Id = null });
deleteUser
.ShouldThrow<UserNotFoundException>()
.WithInnerException<ArgumentNullException>()
.WithInnerMessage("User Id must have value");
您仍将使用 Visual Studio 测试框架,只是您将拥有一个额外的库,用于非常流畅的断言。
另一方面,第一选择需要更多的工作,因为通常是手动解决方案:
try
{
usersRepository.Delete(new User { Id = null });
Assert.Fail("Deleting user with null id should throw");
}
catch (UserNotFoundException ue)
{
Assert.AreEqual(ue.InnerException.Message, "User Id must have value");
}
您将 ExpectedException
属性替换为声明实际异常实例的自定义代码。就像我说的,这是更多的工作,但确实有效。
对于单元测试,我目前使用 FluentAssertions。自从我学会了它,我就再也不想以任何其他方式断言东西了。
要断言异常,请查看 documentation
的这一点
特别是这部分
Action act = () => subject.Foo2("Hello");
act.ShouldThrow<InvalidOperationException>()
.WithInnerException<ArgumentException>()
.WithInnerMessage("whatever")
这是一个老问题,但我想与大家分享我自己对 ExpectedInnerExceptionAttribute
的实现。可能对某人有用
public class ExpectedInnerExceptionAttribute : ExpectedExceptionBaseAttribute
{
public ExpectedInnerExceptionAttribute(Type exceptionType)
{
this.ExceptionType = exceptionType;
}
public Type ExceptionType { get; private set; }
protected override void Verify(Exception ex)
{
if (ex != null && ex.InnerException != null
&& ex.InnerException.GetType() == this.ExceptionType)
{
return;
}
throw ex;
}
}
您也可以扩展它来检查异常消息等。您只需要在 Verify 方法中添加您自己的逻辑。
只需使用 GetAwaiter()
和 GetResult()
来检查内部异常:
Assert.Throws<InnerException>(() => thing.GetAwaiter().GetResult());
例如
Assert.Throws<CommunicationException>(() => thing.GetAwaiter().GetResult());
FluentAssertions 真的很有帮助。
我使用它实现了我的解决方案,如下所示。这是从 AggregateException
检查我的自定义异常 ProcessFailureException
Func<Task> func = async () => await item.ProcessAsync(context, message);
func.Should().Throw<AggregateException>().WithInnerException<ProcessFailureException>();
我正在使用 Visual Studio 的集成框架编写一些单元测试。我需要编写一些测试用例,这些用例在抛出适当的异常时通过。问题是我需要测试的异常是嵌套在更一般的异常中的内部异常。是否有一些简单的解决方案,或者我是否需要扩展整个功能。我目前正在使用 [ExpectedException] 属性,但在这种情况下它不会有多大用处。
我也很好奇当我们使用 [ExpectedException] 时会发生什么,同时我们在测试本身中也有一些 Assert 逻辑。条件是否被评估(抛出异常并且 Assert 语句被证明是有效的)或测试在抛出正确的异常后立即通过?
不是一个完整的解决方案,但在 NUnit 中,您可以做这样的事情:
var ex = Assert.Throws<Exception>(() => thing.ThatThrows());
Assert.That(ex.InnerException, Is.TypeOf<BadException>() );
也许你可以在你的测试框架中?
如果你的框架不支持自定义抛出,你通常有两种选择:
- 自己实现
- 更改(或扩展)框架
我将从第二种解决方案开始。考虑使用 FluentAssertions 库。它允许你做这样的事情:
Action deleteUser = () => usersRepository.Delete(new User { Id = null });
deleteUser
.ShouldThrow<UserNotFoundException>()
.WithInnerException<ArgumentNullException>()
.WithInnerMessage("User Id must have value");
您仍将使用 Visual Studio 测试框架,只是您将拥有一个额外的库,用于非常流畅的断言。
另一方面,第一选择需要更多的工作,因为通常是手动解决方案:
try
{
usersRepository.Delete(new User { Id = null });
Assert.Fail("Deleting user with null id should throw");
}
catch (UserNotFoundException ue)
{
Assert.AreEqual(ue.InnerException.Message, "User Id must have value");
}
您将 ExpectedException
属性替换为声明实际异常实例的自定义代码。就像我说的,这是更多的工作,但确实有效。
对于单元测试,我目前使用 FluentAssertions。自从我学会了它,我就再也不想以任何其他方式断言东西了。
要断言异常,请查看 documentation
的这一点特别是这部分
Action act = () => subject.Foo2("Hello");
act.ShouldThrow<InvalidOperationException>()
.WithInnerException<ArgumentException>()
.WithInnerMessage("whatever")
这是一个老问题,但我想与大家分享我自己对 ExpectedInnerExceptionAttribute
的实现。可能对某人有用
public class ExpectedInnerExceptionAttribute : ExpectedExceptionBaseAttribute
{
public ExpectedInnerExceptionAttribute(Type exceptionType)
{
this.ExceptionType = exceptionType;
}
public Type ExceptionType { get; private set; }
protected override void Verify(Exception ex)
{
if (ex != null && ex.InnerException != null
&& ex.InnerException.GetType() == this.ExceptionType)
{
return;
}
throw ex;
}
}
您也可以扩展它来检查异常消息等。您只需要在 Verify 方法中添加您自己的逻辑。
只需使用 GetAwaiter()
和 GetResult()
来检查内部异常:
Assert.Throws<InnerException>(() => thing.GetAwaiter().GetResult());
例如
Assert.Throws<CommunicationException>(() => thing.GetAwaiter().GetResult());
FluentAssertions 真的很有帮助。
我使用它实现了我的解决方案,如下所示。这是从 AggregateException
ProcessFailureException
Func<Task> func = async () => await item.ProcessAsync(context, message);
func.Should().Throw<AggregateException>().WithInnerException<ProcessFailureException>();