2012-12-29 15 views
18

Il seguente xUnit.net prova asincrona con un lambda contrassegnati con il modificatore async fallisce segnalando che nessun stata generata un'eccezione:Come gestire le eccezioni generate da Task in xUnit .net's Assert.Throws <T>?

[Theory, AutoWebData] 
    public async Task SearchWithNullQueryThrows(
     SearchService sut, 
     CancellationToken dummyToken) 
    { 
     // Fixture setup 
     // Exercise system and verify outcome 
     Assert.Throws<ArgumentNullException>(async() => 
      await sut.SearchAsync(null, dummyToken)); 
     // Teardown 
    } 

Per assicurarsi che un ArgumentNullException è in realtà gettato ho usato in modo esplicito un blocco try-catch. Ha funzionato, ma il codice risultante non è pulito (rispetto alla prima prova):

[Theory, AutoWebData] 
public async Task SearchWithNullQueryThrows(
    SearchService sut, 
    CancellationToken dummyToken) 
{ 
    // Fixture setup 
    var expected = typeof(ArgumentNullException); 
    Type actual = null; 
    // Exercise system 
    try 
    { 
     await sut.SearchAsync(null, dummyToken); 
    } 
    catch (ArgumentNullException e) 
    { 
     actual = e.GetType(); 
    } 
    // Verify outcome 
    Assert.Equal(expected, actual); 
    // Teardown 
} 

Perché la Assert.Throws<T> con la lambda contrassegnato con il modificatore async fallisce?

+3

Questo è un problema noto: http://xunit.codeplex.com/workitem/9799 – DaveShaw

+0

1 E ' sembra essere risolto nello snapshot '03e3be9a6781' che precede lo snapshot' 16883cb2351f' dove è stata associata la versione '2.0.0-alpha'. Tuttavia, non funziona ancora dopo l'aggiornamento della soluzione con NuGet Package Manager. –

risposta

33

Aggiorna

Ciò è stato risolto in xUnit 2, con l'aggiunta di Assert.ThrowsAsync.


sto sospettando che Assert.Throws non è async -consapevoli. Raccomando di sollevare questo problema con il team xUnit, suggerendo di aggiungere ThrowsAsync.

Un async delegato in questo caso sta tornando Task o Task<T>, e il ArgumentNullException non è buttato fuori del delegato direttamente; invece, si trova su Task (Task.Exception.InnerException). Assert.Throws si aspetta che l'eccezione venga espulsa direttamente dal delegato, non posizionata su una proprietà del valore restituito.

È possibile creare il proprio AssertEx.ThrowsAsync come tale:

public static async Task ThrowsAsync<TException>(Func<Task> func) 
{ 
    var expected = typeof(TException); 
    Type actual = null; 
    try 
    { 
    await func(); 
    } 
    catch (Exception e) 
    { 
    actual = e.GetType(); 
    } 
    Assert.Equal(expected, actual); 
} 

che può essere usato come tale:

[Theory, AutoWebData] 
public async Task SearchWithNullQueryThrows(
    SearchService sut, 
    CancellationToken dummyToken) 
{ 
    // Fixture setup 
    // Exercise system and verify outcome 
    await AssertEx.ThrowsAsync<ArgumentNullException>(async() => 
     await sut.SearchAsync(null, dummyToken)); 
    // Teardown 
} 

Io uso un approccio simile in MSTest.

+0

Ha funzionato :) Grazie! –

+0

Ho modificato leggermente il tuo codice, poiché credo che solo "ArgumentNullException" arriverebbe fino al "Assert". Un'altra cosa che potrebbe valere la pena di considerare è che l'originale 'Assert.Throws' * restituisce * l'eccezione (in modo che sia possibile testare anche il messaggio di eccezione, ad esempio). – Benjol

+0

Vedi anche https://gist.github.com/Haacked/4616366 – Benjol

2

Se hai bisogno anche di restituire l'eccezione per verificare che allora questo potrebbe essere utile:

public static async Task<Exception> AssertThrowsAsync<TException>(Func<Task> func) 
    { 
     var expected = typeof (TException); 
     Exception exception = null; 
     Type actual = null; 
     try 
     { 
      await func(); 
     } 
     catch (Exception e) 
     { 
      actual = e.GetType(); 
      exception = e; 
     } 
     Assert.NotNull(exception); 
     Assert.Equal(expected, actual); 
     return exception; 
    } 
Problemi correlati