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

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


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.


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);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.