Asserire un'eccezione utilizzando XUnit


111

Sono un principiante di XUnit e Moq. Ho un metodo che accetta la stringa come argomento.Come gestire un'eccezione usando XUnit.

[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException() {
    //arrange
    ProfileRepository profiles = new ProfileRepository();
    //act
    var result = profiles.GetSettingsForUserID("");
    //assert
    //The below statement is not working as expected.
    Assert.Throws<ArgumentException>(() => profiles.GetSettingsForUserID(""));
}

Metodo in prova

public IEnumerable<Setting> GetSettingsForUserID(string userid)
{            
    if (string.IsNullOrWhiteSpace(userid)) throw new ArgumentException("User Id Cannot be null");
    var s = profiles.Where(e => e.UserID == userid).SelectMany(e => e.Settings);
    return s;
}

1
Cosa intendi con "non funziona come previsto"? (Inoltre, formatta il codice in modo più leggibile. Usa l'anteprima e pubblica quando appare come vorresti che fosse se lo leggessi.)
Jon Skeet

4
Suggerimento: stai chiamando GetSettingsForUserID("")prima di iniziare a chiamare Assert.Throws. La Assert.Throwschiamata non può aiutarti. Suggerirei di essere meno rigido riguardo agli AAA ...
Jon Skeet

Risposte:


183

L' espressione Assert.Throws rileverà l'eccezione e asserirà il tipo. Tuttavia stai chiamando il metodo sotto test al di fuori dell'espressione assert e quindi fallendo il test case.

[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException()
{
    //arrange
    ProfileRepository profiles = new ProfileRepository();
    // act & assert
    Assert.Throws<ArgumentException>(() => profiles.GetSettingsForUserID(""));
}

Se sei deciso a seguire AAA puoi estrarre l'azione nella sua variabile.

[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException()
{
    //arrange
    ProfileRepository profiles = new ProfileRepository();
    //act
    Action act = () => profiles.GetSettingsForUserID("");
    //assert
    var exception = Assert.Throws<ArgumentException>(act);
    //The thrown exception can be used for even more detailed assertions.
    Assert.Equal("expected error message here", exception.Message);
}

Notare come l'eccezione possa essere utilizzata anche per asserzioni dettagliate sulla modalità


5
Se si utilizzano metodi asincroni, Visual Studio genera un avviso con la sintassi precedente. Preferisce questo:async Task act() => await service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);
Alec

5
In realtà per me ciò ha provocato un errore, "non può convertire implicitamente Task in Func <Task>", mentre se lo metto solo Task act() => service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);allora è soddisfatto e funziona bene.
Alec

in che modo lavorare con async / await influisce su questo? quando provo a farlo usando ThrowsAsync nel mio test, non raggiunge mai la linea Assert.Equal poiché genera correttamente l'errore e chiude il test. testare l'acqua per vedere se questa dovrebbe essere una nuova domanda ...
nathanjw

@AlecDenholm Grazie! Questa è l'unica cosa che ha funzionato per me. Penso che alcuni degli altri suggerimenti non funzionino correttamente per le cose asincrone.
marchio

45

Se vuoi essere rigido su AAA, puoi usare Record.Exception da xUnit per catturare l'eccezione nella tua fase Act.

È quindi possibile effettuare asserzioni basate sull'eccezione acquisita nella fase Asserzione.

Un esempio di ciò può essere visto nei test xUnits .

[Fact]
public void Exception()
{
    Action testCode = () => { throw new InvalidOperationException(); };

    var ex = Record.Exception(testCode);

    Assert.NotNull(ex);
    Assert.IsType<InvalidOperationException>(ex);
}

Dipende da te quale percorso desideri seguire ed entrambi i percorsi sono completamente supportati da ciò che fornisce xUnit.


1
FWIW, questa soluzione è ottima se hai bisogno di convalidare il messaggio di eccezione, ecc. Penso che sia quando potresti usare Record.Exception.
Jeff LaFay

@JeffLaFay Apprezzo di essere un po 'in ritardo alla festa qui, in che modo sarebbe diverso dall'uso var exception = Assert.Throws<InvalidOperationException>(testCode);e dall'affermazione exception.Message? o è solo un altro modo per ottenere la stessa cosa?
ColinM

3

Potresti prendere in considerazione qualcosa di simile se vuoi attenersi ad AAA:

// Act 
Task act() => handler.Handle(request);

// Assert
await Assert.ThrowsAsync<MyExpectedException>(act);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.