Aktivieren Sie eine Ausnahme mit XUnit

109

Ich bin ein Neuling bei XUnit und Moq. Ich habe eine Methode, die Zeichenfolge als Argument verwendet. Wie man eine Ausnahme mit XUnit behandelt.

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

Testmethode

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;
}
Wandermonk
quelle
1
Was meinst du mit "funktioniert nicht wie erwartet"? (Bitte formatieren Sie Ihren Code auch besser lesbar. Verwenden Sie die Vorschau und veröffentlichen Sie, wenn es so aussieht, wie es aussehen soll, wenn Sie es lesen.)
Jon Skeet
4
Hinweis: Sie rufen an, GetSettingsForUserID("")bevor Sie anrufen Assert.Throws. Der Assert.ThrowsAnruf kann Ihnen dort nicht helfen. Ich würde vorschlagen, weniger starr über AAA zu sein ...
Jon Skeet

Antworten:

182

Der Assert.Throws- Ausdruck fängt die Ausnahme ab und bestätigt den Typ. Sie rufen jedoch die zu testende Methode außerhalb des Assert-Ausdrucks auf und schlagen damit den Testfall nicht.

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

Wenn Sie AAA folgen möchten, können Sie die Aktion in eine eigene Variable extrahieren.

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

Beachten Sie, wie die Ausnahme auch für detaillierte Zusicherungen im Modus verwendet werden kann

Nkosi
quelle
5
Bei Verwendung asynchroner Methoden gibt Visual Studio eine Warnung mit der obigen Syntax aus. Es bevorzugt dies:async Task act() => await service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);
Alec
5
Eigentlich hat das für mich zu einem Fehler geführt: 'Task kann nicht implizit in Func <Aufgabe> konvertiert werden', wohingegen es, wenn ich es nur sage, Task act() => service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);damit zufrieden ist und gut funktioniert.
Alec
Wie wirkt sich die Arbeit mit async / await darauf aus? Wenn ich dies mit ThrowsAsync in meinem Test versuche, erreicht es nie die Assert.Equal-Zeile, da es den Fehler erfolgreich auslöst und den Test beendet. Testen Sie das Wasser, um zu sehen, ob dies eine neue Frage sein sollte ...
Nathanjw
@AlecDenholm Danke! Das ist das einzige, was für mich funktioniert hat. Ich denke, einige der anderen Vorschläge funktionieren bei asynchronen Dingen nicht richtig.
Marke
44

Wenn Sie in Bezug auf AAA starr sein möchten, können Sie Record.Exception von xUnit verwenden, um die Ausnahme in Ihrer Act-Phase zu erfassen.

Sie können dann Assertions basierend auf der erfassten Ausnahme in der Assert-Phase erstellen.

Ein Beispiel hierfür sind xUnits-Tests .

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

    var ex = Record.Exception(testCode);

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

Es liegt an Ihnen, welchem ​​Pfad Sie folgen möchten, und beide Pfade werden von xUnit vollständig unterstützt.

Bhargav Rao
quelle
1
FWIW, Diese Lösung ist großartig, wenn Sie möglicherweise die Ausnahmemeldung usw. validieren müssen. Ich denke, dann könnten Sie Record.Exception verwenden.
Jeff LaFay
@ JeffLaFay Ich schätze, ich bin ein bisschen zu spät zur Party hier. Wie würde sich das von der Verwendung var exception = Assert.Throws<InvalidOperationException>(testCode);und Behauptung unterscheiden exception.Message? oder ist es nur eine andere Art, dasselbe zu erreichen?
ColinM
3

Sie könnten so etwas in Betracht ziehen, wenn Sie sich an AAA halten möchten:

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

// Assert
await Assert.ThrowsAsync<MyExpectedException>(act);
Yves Rochon
quelle