8

今天我读了很多关于 async/await 的内容,这让我大吃一惊。我不明白为什么以下测试通过了。

[Test]
public void Test()
{
    var listener = new AsyncHttpListener();
    listener.ListeningAsync();

    try
    {
        new WebClient().DownloadString("http://localhost:8080/");
    }
    catch (Exception)
    {
    }

    listener.Close();
}

public class AsyncHttpListener
{
    private readonly HttpListener listener;

    public AsyncHttpListener()
    {
        listener = new HttpListener();
        listener.Prefixes.Add("http://localhost:8080/");
        listener.Start();
    }

    public void Close()
    {
        listener.Close();
    }

    public async void ListeningAsync()
    {
        var context = await listener.GetContextAsync();
        HandleContext(context);
    }

    private void HandleContext(HttpListenerContext context)
    {
        throw new Exception("test excpetion");
    }
}


测试通过,但输出包含:

系统异常
考试成绩
   在 AsyncHttpListener.cs 中的 AsyncHttpListenerTest.AsyncHttpListener.HandleContext(HttpListenerContext context):第 30 行
   在 AsyncHttpListener.cs 中的 AsyncHttpListenerTest.AsyncHttpListener.d__0.MoveNext():第 25 行
--- 从先前抛出异常的位置结束堆栈跟踪 ---
   在 System.Runtime.CompilerServices.AsyncMethodBuilderCore.b__1(对象状态)
   在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext,ContextCallback 回调,对象状态,布尔值 preserveSyncCtx)
   在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback 回调,对象状态,布尔值 preserveSyncCtx)
   在 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   在 System.Threading.ThreadPoolWorkQueue.Dispatch()

我希望异常会从任务线程(HandleContext() 方法)传输到调用者上下文并且测试失败。我怎样才能得到这种行为?

4

1 回答 1

13

用你的方法async Task代替async void,用你的测试方法async Task代替void

public async Task ListeningAsync()
{
    var context = await listener.GetContextAsync();
    HandleContext(context);
}

[Test]
public async Task Test()
{
    var listener = new AsyncHttpListener();
    await listener.ListeningAsync();

    try
    {
        new WebClient().DownloadString("http://localhost:8080/");
    }
    catch (Exception)
    {
    }

    listener.Close();
}

有几个很好的理由可以避免async void。错误处理就是其中之一。方法引发的错误async void直接指向SynchronizationContext方法启动时的错误。

您的测试通过的原因是async方法可能在完成之前返回给调用者。测试运行程序看到测试方法返回(还没有抛出异常),并将其标记为“通过”。如果你从你的测试方法返回,那么测试运行者知道在考虑测试完成之前Task等待完成。Task

于 2013-01-17T13:25:31.883 回答