Afirmar una excepción usando XUnit

111

Soy un novato en XUnit y Moq. Tengo un método que toma una cadena como argumento. Cómo manejar una excepción 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 bajo prueba

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
fuente
1
¿Qué quiere decir con "no funciona como se esperaba"? (Además, formatee su código de manera más legible. Use la vista previa y publíquelo cuando se vea como le gustaría que se vea si lo estuviera leyendo).
Jon Skeet
4
Pista: estás llamando GetSettingsForUserID("")antes de empezar a llamar Assert.Throws. La Assert.Throwsllamada no puede ayudarte en eso. Sugeriría ser menos rígido sobre AAA ...
Jon Skeet

Respuestas:

183

La expresión Assert.Throws detectará la excepción y afirmará el tipo. Sin embargo, está llamando al método bajo prueba fuera de la expresión de aserción y, por lo tanto, falla el caso de prueba.

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

Si está decidido a seguir AAA, puede extraer la acción en su propia variable.

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

Tenga en cuenta que la excepción también se puede utilizar para aserciones detalladas de modo

Nkosi
fuente
5
Si usa métodos asíncronos, Visual Studio muestra una advertencia con la sintaxis anterior. Prefiere esto:async Task act() => await service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);
Alec
5
En realidad, para mí, eso resultó en un error, 'no puedo convertir implícitamente Task a Func <Task>', mientras que si lo pongo Task act() => service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);, está contento con eso y funciona bien.
Alec
¿Cómo afecta esto trabajar con async / await? cuando intento hacer esto usando ThrowsAsync en mi prueba, nunca llega a la línea Assert.Equal, ya que arroja con éxito el error y sale de la prueba. probando el agua para ver si esto debería ser una nueva pregunta ...
nathanjw
@AlecDenholm ¡Gracias! Eso es lo único que funcionó para mí. Creo que algunas de las otras sugerencias realmente no funcionan correctamente para cosas asincrónicas.
marca registrada
45

Si desea ser rígido con respecto a AAA, puede usar Record.Exception de xUnit para capturar la excepción en su etapa Act.

Luego, puede realizar afirmaciones basadas en la excepción capturada en la etapa Assert.

Un ejemplo de esto se puede ver en las pruebas de xUnits .

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

    var ex = Record.Exception(testCode);

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

Depende de usted qué camino desea seguir, y ambos caminos son totalmente compatibles con lo que proporciona xUnit.

Bhargav Rao
fuente
1
FWIW, esta solución es excelente si necesita validar el mensaje de excepción, etc. Creo que es entonces cuando puede usar Record.Exception.
Jeff LaFay
@JeffLaFay Aprecio que llegué un poco tarde a la fiesta aquí, ¿en qué se diferenciaría de usar var exception = Assert.Throws<InvalidOperationException>(testCode);y afirmar exception.Message? ¿O es solo otro sabor de lograr lo mismo?
ColinM
3

Podría considerar algo como esto si desea ceñirse a AAA:

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

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