2013-01-17 3 views
7

Heute lese ich viel über async/warte und es hat mich völlig umgehauen. Ich kann nicht verstehen, warum der folgende Test bestanden hat.Wie eine Ausnahme von der asynchronen Methode zum Aufrufer zu werfen?

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


Test bestanden, aber Ausgabe enthält:

 
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() 

Ich erwarte, dass Ausnahme wird von Task-Thread (HandleContext() -Methode) übertragen werden Kontext und Test an Anrufer scheitern. Wie kann ich dieses Verhalten bekommen?

Antwort

10

Machen Sie Ihre Methode async Task statt async void und Ihre Testmethode async Task statt void machen:

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(); 
} 

Es gibt mehrere gute Gründe async void zu vermeiden. Fehlerbehandlung ist einer von ihnen. Fehler, die von async void Methoden ausgelöst werden, gehen direkt zu der SynchronizationContext, die aktuell war, als die Methode gestartet wurde.

Der Grund dafür, dass Ihr Test bestanden hat, ist, dass async Methoden zu ihrem Aufrufer zurückkehren können, bevor sie abgeschlossen sind. Der Test-Runner sieht die Testmethode zurück (ohne noch eine Ausnahme auszulösen) und markiert sie als "bestanden". Wenn Sie Task von Ihrer Testmethode zurückgeben, dann weiß der Test-Runner, dass der Task abgeschlossen sein muss, bevor der Test abgeschlossen wird.