Afirmar uma exceção usando XUnit

111

Eu sou um novato em XUnit e Moq. Eu tenho um método que leva string como argumento.Como lidar com uma exceção 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(""));
}

Método em teste

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
fonte
1
O que você quer dizer com "não está funcionando conforme o esperado"? (Além disso, formate seu código de forma mais legível. Use a visualização e poste quando ficar com a aparência que você gostaria que estivesse lendo.)
Jon Skeet
4
Dica: você está ligando GetSettingsForUserID("")antes de começar a ligar Assert.Throws. A Assert.Throwsligação não pode te ajudar nisso. Eu sugiro ser menos rígido sobre AAA ...
Jon Skeet

Respostas:

183

A expressão Assert.Throws capturará a exceção e declarará o tipo. No entanto, você está chamando o método em teste fora da expressão assert e, portanto, falhando no caso de teste.

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

Se estiver determinado a seguir AAA, você pode extrair a ação em sua própria variável.

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

Observe como a exceção também pode ser usada para afirmações detalhadas de modo

Nkosi
fonte
5
Se estiver usando métodos assíncronos, o Visual Studio emite um aviso com a sintaxe acima. Ele prefere este:async Task act() => await service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);
Alec
5
Na verdade, para mim, isso resultou em um erro, 'não é possível converter implicitamente a tarefa em Func <Task>', ao passo que, se eu apenas colocar Task act() => service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);, ficará satisfeito com isso e funcionará bem.
Alec
como trabalhar com async / await afeta isso? quando tento fazer isso usando ThrowsAsync em meu teste, ele nunca atinge a linha Assert.Equal, pois gera o erro e sai do teste com êxito. testando a água para ver se essa seria uma pergunta nova ...
nathanjw
@AlecDenholm Obrigado! Essa é a única coisa que funcionou para mim. Acho que algumas das outras sugestões não funcionam corretamente para coisas assíncronas.
marca registrada
45

Se você quiser ser rígido sobre AAA, pode usar Record.Exception from xUnit para capturar a exceção em seu estágio Act.

Você pode então fazer asserções com base na exceção capturada no estágio Assert.

Um exemplo disso pode ser visto nos testes xUnits .

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

    var ex = Record.Exception(testCode);

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

Depende de você qual caminho deseja seguir, e ambos os caminhos são totalmente suportados pelo que o xUnit fornece.

Bhargav Rao
fonte
1
FWIW, essa solução é ótima se você precisar talvez validar a mensagem de exceção, etc. Acho que é quando você pode usar Record.Exception.
Jeff LaFay,
@JeffLaFay Agradeço que estou um pouco atrasado para a festa aqui, como isso seria diferente de usar var exception = Assert.Throws<InvalidOperationException>(testCode);e afirmar exception.Message? ou é apenas mais um sabor de alcançar a mesma coisa?
ColinM de
3

Você pode considerar algo assim se quiser manter o AAA:

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

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