Утверждение исключения с помощью XUnit

111

Я новичок в XUnit и Moq. У меня есть метод, который принимает строку в качестве аргумента. Как обработать исключение с помощью 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(""));
}

Тестируемый метод

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
Что вы имеете в виду под «не работает должным образом»? (Кроме того, отформатируйте свой код более читабельно. Используйте предварительный просмотр и опубликуйте, когда он будет выглядеть так, как вы хотели бы, если бы вы его читали.)
Джон Скит,
4
Подсказка: вы звоните GetSettingsForUserID("")до того, как начнете звонить Assert.Throws. Assert.ThrowsВызов не может помочь. Я бы посоветовал быть менее жестким в отношении AAA ...
Джон Скит

Ответы:

186

Assert.Throws выражение будет ловить исключение и утверждать тип. Однако вы вызываете тестируемый метод вне выражения assert и, таким образом, не выполняете тестовый пример.

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

Если вы хотите следовать AAA, вы можете извлечь действие в его собственную переменную.

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

Обратите внимание, как исключение также можно использовать для подробных утверждений режима

Nkosi
источник
5
При использовании асинхронных методов Visual Studio выдает предупреждение с указанным выше синтаксисом. Он предпочитает это:async Task act() => await service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);
Алек
5
На самом деле для меня это привело к ошибке: «не может неявно преобразовать Task в Func <Task>», тогда как если я просто поставлю, Task act() => service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);то он доволен этим и отлично работает.
Alec
как на это влияет работа с async / await? когда я пытаюсь сделать это с помощью ThrowsAsync в моем тесте, он никогда не достигает строки Assert.Equal, поскольку он успешно выдает ошибку и завершает тест. проверить воду, чтобы увидеть, должен ли это быть новый вопрос ...
Натанжв,
@AlecDenholm Спасибо! Это единственное, что у меня сработало. Я думаю, что некоторые из других предложений действительно не работают должным образом для асинхронных материалов.
товарный знак
45

Если вы действительно хотите быть жесткими в отношении AAA, вы можете использовать Record.Exception из xUnit для захвата исключения на этапе действия.

Затем вы можете делать утверждения на основе зафиксированного исключения на этапе Assert.

Пример этого можно увидеть в тестах xUnits .

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

    var ex = Record.Exception(testCode);

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

Вам решать, по какому пути вы хотите идти, и оба пути полностью поддерживаются тем, что предоставляет xUnit.

Бхаргав Рао
источник
1
FWIW, это решение отлично, если вам, возможно, нужно проверить сообщение об исключении и т. Д. Я думаю, что тогда вы можете использовать Record.Exception.
Джефф ЛаФэй
@JeffLaFay Я понимаю, что немного опоздал на вечеринку, чем это будет отличаться от использования var exception = Assert.Throws<InvalidOperationException>(testCode);и утверждения exception.Message? или это просто еще один способ достижения того же?
ColinM
3

Вы можете подумать о чем-то вроде этого, если хотите придерживаться AAA:

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

// Assert
await Assert.ThrowsAsync<MyExpectedException>(act);
Ив Рошон
источник