java - org.junit.jupiter.api.test maven
JUnit 5: ¿Cómo hacer valer una excepción? (9)
¿Hay una mejor manera de afirmar que un método arroja una excepción en JUnit 5?
Actualmente, tengo que usar una @Rule para verificar que mi prueba arroje una excepción, pero esto no funciona para los casos en los que espero que varios métodos arrojen excepciones en mi prueba.
Ahora Junit5 proporciona una forma de hacer valer las excepciones.
Puede probar tanto las excepciones generales como las personalizadas.
Un escenario de excepción general:
ExpectGeneralException.java
public void validateParameters(Integer param ) {
if (param == null) {
throw new NullPointerException("Null parameters are not allowed");
}
}
ExpectGeneralExceptionTest.java
@Test
@DisplayName("Test assert NullPointerException")
void testGeneralException(TestInfo testInfo) {
final ExpectGeneralException generalEx = new ExpectGeneralException();
NullPointerException exception = assertThrows(NullPointerException.class, () -> {
generalEx.validateParameters(null);
});
assertEquals("Null parameters are not allowed", exception.getMessage());
}
Puede encontrar una muestra para probar CustomException aquí: afirmar una muestra de código de excepción
ExpectCustomException.java
public String constructErrorMessage(String... args) throws InvalidParameterCountException {
if(args.length!=3) {
throw new InvalidParameterCountException("Invalid parametercount: expected=3, passed="+args.length);
}else {
String message = "";
for(String arg: args) {
message += arg;
}
return message;
}
}
ExpectCustomExceptionTest.java
@Test
@DisplayName("Test assert exception")
void testCustomException(TestInfo testInfo) {
final ExpectCustomException expectEx = new ExpectCustomException();
InvalidParameterCountException exception = assertThrows(InvalidParameterCountException.class, () -> {
expectEx.constructErrorMessage("sample ","error");
});
assertEquals("Invalid parametercount: expected=3, passed=2", exception.getMessage());
}
Aquí hay una manera fácil.
@Test
void exceptionTest() {
try{
model.someMethod("invalidInput");
fail("Exception Expected!");
}
catch(SpecificException e){
assertTrue(true);
}
catch(Exception e){
fail("wrong exception thrown");
}
}
Solo tiene éxito cuando se lanza la excepción que espera.
Creo que este es un ejemplo aún más simple.
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
....
@Test
void exceptionTesting() {
Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
throw new IllegalArgumentException("a message");
});
assertEquals("a message", exception.getMessage());
}
Llamar a
get()
en un opcional que contiene una
ArrayList
vacía arrojará una
NoSuchElementException
.
assertThrows
declara la excepción esperada y proporciona un proveedor lambda (no toma argumentos y devuelve un valor).
Gracias a @prime por su respuesta que espero haber explicado.
En Java 8 y JUnit 5 (Júpiter) podemos hacer valer las excepciones de la siguiente manera.
Usando
org.junit.jupiter.api.Assertions.assertThrows
public static <T extend Throwable> T ClaimThrows (Class <T> shownType, ejecutable ejecutable)
Afirma que la ejecución del ejecutable proporcionado genera una excepción del tipo esperado y devuelve la excepción.
Si no se lanza una excepción, o si se lanza una excepción de un tipo diferente, este método fallará.
Si no desea realizar verificaciones adicionales en la instancia de excepción, simplemente ignore el valor de retorno.
@Test
public void itShouldThrowNullPointerExceptionWhenBlahBlah() {
assertThrows(NullPointerException.class,
()->{
//do whatever you want to do here
//ex : objectName.thisMethodShoulThrowNullPointerExceptionForNullParameter(null);
});
}
Ese enfoque utilizará el
Executable
interfaz funcional en
org.junit.jupiter.api
.
Referir:
- http://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions
- http://junit.org/junit5/docs/5.0.0-M2/api/org/junit/jupiter/api/Executable.html
- http://junit.org/junit5/docs/5.0.0-M4/api/org/junit/jupiter/api/Assertions.html#assertThrows-java.lang.Class-org.junit.jupiter.api.function.Executable-
En realidad, creo que hay un error en la documentación de este ejemplo en particular. El método que se pretende es esperar.
public static void assertThrows(
public static <T extends Throwable> T expectThrows(
Hay 3 formas de afirmar una cierta excepción en Junit. Escribamos los casos de prueba unitaria para ello.
1. modismo try-catch Este modismo es uno de los más populares porque ya se usó en JUnit 3. Este enfoque es un patrón común. La prueba fallará cuando no se produzca una excepción y la excepción misma se verifique en una cláusula catch.
@Test
public void convertIntoUpperCase_withInvalidInput_tryCatchIdiom() {
try {
exceptionHandling.convertIntoUpperCase("");
fail("It should throw IllegalArgumentException");
} catch (IllegalArgumentException e) {
Assertions.assertThat(e)
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Empty value is passed.");
}
}
2. Anotación esperada de @Test En este enfoque, especificamos la excepción esperada en @Test como se muestra a continuación en @Test (esperado = IllegalArgumentException.class)
Cuando no se lanzó la excepción, recibirá el siguiente mensaje: java.lang.AssertionError: Excepción esperada: java.lang.IllegalArgumentException
Sin embargo, con este enfoque, debe tener cuidado. A veces es tentador esperar Excepción general, RuntimeException o incluso Throwable. ¡Y esto se considera una mala práctica porque su código puede arrojar una excepción en otros lugares de los que realmente esperaba y su prueba aún pasará!
Uno de los inconvenientes de este enfoque es que no puede afirmar el mensaje de excepción.
@Test(expected = IllegalArgumentException.class)
public void convertIntoUpperCase_withInvalidInput_testExpected() {
exceptionHandling.convertIntoUpperCase("");
}
3. Junit @Rule Se puede crear el mismo ejemplo usando la regla ExceptedException. La regla debe ser un campo público marcado con la anotación @Rule.
@Test
public void convertIntoUpperCase_withInvalidInput_ExpectedExceptionRule() {
exception.expect(IllegalArgumentException.class);
exception.expectMessage("Empty value is passed.");
exceptionHandling.convertIntoUpperCase("");
}
Encuentro el código anterior más legible, por lo tanto, prefiero usar este enfoque.
Cuando no se lanza la excepción, recibirá el siguiente mensaje: java.lang.AssertionError: Prueba esperada para lanzar (una instancia de java.lang.IllegalArgumentException y una excepción con el mensaje "Se pasa el valor vacío"). Bastante agradable.
Pero no todas las excepciones verifico con el enfoque anterior. A veces necesito verificar solo el tipo de excepción lanzada y luego uso la anotación @Test.
Lo han cambiado en JUnit 5 (esperado: InvalidArgumentException, real: método invocado) y el código se ve así:
@Test
public void wrongInput() {
Throwable exception = assertThrows(InvalidArgumentException.class,
()->{objectName.yourMethod("WRONG");} );
}
Puede usar
assertThrows()
, que le permite probar varias excepciones dentro de la misma prueba.
Con soporte para lambdas en Java 8, esta es la forma canónica de probar excepciones en JUnit.
Según los documentos de JUnit :
import static org.junit.jupiter.api.Assertions.assertThrows;
@Test
void exceptionTesting() {
MyException thrown =
assertThrows(MyException.class,
() -> myObject.doThing(),
"Expected doThing() to throw, but it didn''t");
assertTrue(thrown.getMessage().contains("Stuff"));
}
Puede usar
assertThrows()
.
Mi ejemplo está tomado de los documentos
http://junit.org/junit5/docs/current/user-guide/
List<String> emptyList = new ArrayList<>();
Optional<String> opt2 = emptyList.stream().findFirst();
assertThrows(NoSuchElementException.class, () -> opt2.get());