.NET中的异步方法异常处理

随着.NET社区对Task Parallel Library (TPL)的逐渐熟悉,其带来的语言特性也变得越来越流行。其中,异常处理是一个显著的区别。在任务中运行的用户代码抛出的未处理异常会传播回加入的线程。为了将所有异常传播回调用线程,Task基础设施将它们包装在一个AggregateException实例中。这种处理方式清晰且直接,具体的详细信息可以在MSDN上找到。

当开始为异步方法编写单元测试时,发现不能再使用通常的技术了。为了断言在特定情况下某个单元抛出了特定的异常,总是使用ExpectedException属性。为了更清楚地说明这一点,将给出一个使用示例。

假设有以下类:

public bool MySimpleMethod(List param) { if (param == null) { throw new ArgumentNullException("param"); } return true; }

将编写一个测试,以断言在将null作为参数传递给此方法调用时,该方法应该引发ArgumentNullException。为此,请考虑以下代码:

[TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void MySimpleMethod_Throws_ArgumentNullException() { sut.MySimpleMethod(null); }

一旦执行,这个测试将通过。现在让编写上述方法的异步版本。

public async Task MySimpleMethodAsync(List param) { if (param == null) { throw new ArgumentNullException("param"); } await Task.Delay(100); return true; }

由于知道异常被包装在AggregateException中,因此预期测试将失败。那么,应该如何测试这种情况呢?使用代码Wait,非常喜欢ExpectedException属性,希望有一个属性可以检查任何聚合的异常是否是期望的类型。

经过快速搜索,没有找到任何适合东西。那一刻,ExpectedAggregateExceptionAttribute诞生了。

遵循了ExpectedException的模式,这就是想到的。

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public sealed class ExpectedAggregateExceptionAttribute : ExpectedExceptionBaseAttribute { // ... }

简而言之,检查抛出的异常是否为AggregateException类型,如果是,再次检查任何内部异常是否为请求的类型。如果这个条件不匹配,将抛出一个新的异常,这将使单元测试失败。如果现在使用新属性而不是ExpectedExceptionAttribute,这个测试将成功。

[TestMethod] [ExpectedAggregateException(typeof(ArgumentNullException))] public void MySimpleMethodAsync_Throws_ArgumentNullException_2() { sut.MySimpleMethodAsync(null).Wait(); }

为了确保新属性正常工作,将进行一些其他测试。首先,期望如果抛出了异常,单元测试将失败:

[TestMethod] [ExpectedAggregateException(typeof(ArgumentNullException))] public void ArgumentNullException_Fail_If_No_Exception() { sut.MySimpleMethodAsync(new List()).Wait(); }

请注意,这个测试不应该通过(不要将其包含在解决方案中)。

同样,如果内部异常中没有预期的异常类型,测试应该失败:

[TestMethod] [ExpectedAggregateException(typeof(ArgumentNullException))] public void ArgumentNullException_Fail_If_Wrong_Inner_Exception() { Task.Factory.StartNew(() => { throw new ArgumentOutOfRangeException(); }); }

由于两个测试都失败了,可以得出结论,这个新属性按预期工作。可以将其打包为想要的方式,在自己的公共库中,在新的或现有的自定义测试库中,或在任何项目中。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485