Potwierdź wyjątek za pomocą XUnit

111

Jestem nowicjuszem w XUnit i Moq. Mam metodę, która przyjmuje ciąg jako argument. Jak obsłużyć wyjątek za pomocą 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(""));
}

Testowana metoda

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
źródło
1
Co masz na myśli, mówiąc „nie działa zgodnie z oczekiwaniami”? (Ponadto sformatuj swój kod bardziej czytelnie. Skorzystaj z podglądu i opublikuj, gdy będzie wyglądał tak, jak chcesz, gdybyś go czytał.)
Jon Skeet,
4
Wskazówka: dzwonisz, GetSettingsForUserID("")zanim zaczniesz dzwonić Assert.Throws. Assert.ThrowsPołączenie nie może pomóc tam. Proponuję być mniej sztywnym w kwestii AAA ...
Jon Skeet,

Odpowiedzi:

184

Assert.Throws wyrażenie złapie wyjątek i dochodzić typ. Jednak wywołujesz testowaną metodę poza wyrażeniem assert, co powoduje niepowodzenie przypadku testowego.

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

Jeśli zdecydujesz się podążać za AAA, możesz wyodrębnić akcję do własnej zmiennej.

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

Zwróć uwagę, jak wyjątek może być również używany dla szczegółowych potwierdzeń trybu

Nkosi
źródło
5
W przypadku korzystania z metod asynchronicznych program Visual Studio zgłasza ostrzeżenie o powyższej składni. Woli to:async Task act() => await service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);
Alec
5
Właściwie dla mnie spowodowało to błąd, „nie można niejawnie przekonwertować Task na Func <Task>”, podczas gdy jeśli po prostu wstawię, Task act() => service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);to jest z tego zadowolony i działa dobrze.
Alec
jaki ma na to wpływ praca z async / await? kiedy próbuję to zrobić przy użyciu ThrowsAsync w moim teście, nigdy nie osiąga linii Assert.Equal, ponieważ pomyślnie zgłasza błąd i kończy test. testowanie wody, aby zobaczyć, czy to powinno być nowe pytanie ...
nathanjw
@AlecDenholm Thanks! To jedyna rzecz, która mi pomogła. Myślę, że niektóre inne sugestie tak naprawdę nie działają poprawnie w przypadku rzeczy asynchronicznych.
znak towarowy
45

Jeśli chcesz być sztywny w kwestii AAA, możesz użyć Record.Exception z xUnit, aby uchwycić wyjątek na etapie aktu.

Następnie można tworzyć potwierdzenia na podstawie przechwyconego wyjątku na etapie Assert.

Przykład tego można zobaczyć w testach xUnits .

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

    var ex = Record.Exception(testCode);

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

To od Ciebie zależy, jaką ścieżką chcesz podążać, a obie ścieżki są w pełni obsługiwane przez to, co zapewnia xUnit.

Bhargav Rao
źródło
1
FWIW, To rozwiązanie jest świetne, jeśli chcesz może zweryfikować komunikat o wyjątku, itp. Myślę, że wtedy możesz użyć Record.Exception.
Jeff LaFay
@JeffLaFay Doceniam, że jestem trochę spóźniony na imprezę tutaj, czym by się to różniło od używania var exception = Assert.Throws<InvalidOperationException>(testCode);i potwierdzania exception.Message? czy jest to po prostu kolejny smak osiągnięcia tego samego?
ColinM
3

Możesz rozważyć coś takiego, jeśli chcesz trzymać się AAA:

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

// Assert
await Assert.ThrowsAsync<MyExpectedException>(act);
Yves Rochon
źródło