如何在异步方法中测试异常

时间:2012-03-03 05:05:09

标签: .net async-ctp visual-studio-2012

我正在玩Visual Studio 11 Beta。

鉴于此代码:

namespace KC.DataAccess.Global
{
    /// <summary>Global methods for SQL access</summary>
    public static class SQL
    {    
        public async static void ExecuteNonQuery(string ConnStr, string Query)
        {
            if (string.IsNullOrEmpty(ConnStr)) throw new ArgumentNullException("ConnStr");
            if (string.IsNullOrEmpty(Query)) throw new ArgumentNullException("Query");
            SqlConnection conn = new SqlConnection(ConnStr);
            SqlCommand cmd = PrepSqlConnection(ref conn, Query);
            Exception exc = null;
            for (int i = 0; i < 3; i++)
                try { await Task.Run(() => cmd.ExecuteNonQuery()); break; }
                catch (Exception ex) { Thread.Sleep(50); exc = ex; }
            if (exc != null) throw new ApplicationException("Command failed after maximum attempts", exc);
            conn.Close();
            conn.Dispose();
         }
    } 
} 

由于它是一个异步方法,异常似乎没有冒泡到调用方法。我的测试用例因此失败了:

using Target = KC.DataAccess.Global.SQL;
[TestMethod]
[TestCategory("Unit")]
[ExpectedException(typeof(ArgumentNullException))]
public void ExecuteNonQueryFail1()
{
    Target.ExecuteNonQuery(null, "select 1");
}

在这种情况下,ExecuteNonQuery的验证部分显然抛出异常,我看到它在调试时抛出。

我已将测试方法更改为async,语法为 await Task.Run(()=&gt; Target.ExecuteNonQuery()),但无济于事。

问题:

  • ExecuteNonQuery是否会抛出异常?
  • 为什么ExecuteNonQueryFail1看不到异常?
  • 如何更改测试方法或方法本身,以正确处理异常并传递测试用例,而不放弃方法的异步性质?

2 个答案:

答案 0 :(得分:4)

VS11 Beta对返回Task的测试方法提供了一流的支持。

因此,如果您将签名更改为:

public async static Task ExecuteNonQuery(string ConnStr, string Query)

然后你可以测试它:

using Target = KC.DataAccess.Global.SQL;
[TestMethod]
[TestCategory("Unit")]
[ExpectedException(typeof(ArgumentNullException))]
public async Task ExecuteNonQueryFail1()
{
  await Target.ExecuteNonQuery(null, "select 1");
}

(我没有机会亲自尝试这种新的支持,但我已经读过这应该有用了。)

注意:除非必须返回Task,否则应该async方法中返回void(例如,对于某个事件)处理程序)。 Task是等待的,因此代码更具可重用性。我在my "Async and Await" intro blog post中介绍了这一点。

如果您确实需要出于某种原因测试async void方法,则需要提供自己的SynchronizationContext以捕获任何异常(请参阅Stephen Toub's recent blog post的“异步单元测试”部分)。

我有一些专门处理异步单元测试的博客文章(part 1part 2)。我为VS2010 + AsyncCTP或VS11-DevPreview编写了一个基本的异步单元测试项目,但我还没有机会用VS11-Beta测试它。如果需要,这将是单元测试async void方法的最简单方法。 [CodePlex | NuGet]

答案 1 :(得分:3)

由于您的方法返回void,因此异常无法传播到调用代码。如果将返回类型更改为Task,现在可以观察异常,但必须明确地执行此操作。

我认为修改调用代码的最佳方法就是调用Wait()。如果Task中的代码引发了异常,则Wait()会抛出一个包含原始异常的AggregateException