2013-01-17 72 views
7

今天我读了很多关于异步/等待的信息,它完全让我失去了主意。 我不明白为什么下面的测试通过。如何从异步方法向调用者抛出异常?

[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"); 
    } 
} 


测试通过,但输出包含:

 
System.Exception 
test excpetion 
    at AsyncHttpListenerTest.AsyncHttpListener.HandleContext(HttpListenerContext context) in AsyncHttpListener.cs: line 30 
    at AsyncHttpListenerTest.AsyncHttpListener.d__0.MoveNext() in AsyncHttpListener.cs: line 25 
--- End of stack trace from previous location where exception was thrown --- 
    at System.Runtime.CompilerServices.AsyncMethodBuilderCore.b__1(Object state) 
    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
    at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() 
    at System.Threading.ThreadPoolWorkQueue.Dispatch() 

我期望例外将从任务线程传输(HandleContext()方法)至呼叫者上下文和测试失败。我如何得到这种行为?

回答

10

让你的方法async Task而不是async void,并让您的测试方法,而不是async Taskvoid

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完成。