1. Introducción
En este artículo, exploraremos en detalle las afirmaciones disponibles en JUnit.
Después de la migración de los artículos JUnit 4 a JUnit 5 y A Guide to JUnit 5, ahora vamos a entrar en detalles sobre las diferentes afirmaciones disponibles en JUnit 4 y JUnit 5.
También destacaremos las mejoras realizadas en las afirmaciones con JUnit 5.
2. Afirmaciones
Las afirmaciones son métodos de utilidad para respaldar la afirmación de condiciones en las pruebas ; estos métodos son accesibles a través de la clase Assert , en JUnit 4, y Assertions , en JUnit 5.
Para aumentar la legibilidad de la prueba y de las afirmaciones en sí, siempre se recomienda importar estáticamente la clase respectiva. De esta manera, podemos referirnos directamente al método de aserción en sí mismo sin la clase de representación como prefijo.
Comencemos a explorar las afirmaciones disponibles con JUnit 4.
3. Afirmaciones en JUnit 4
En esta versión de la biblioteca, las aserciones están disponibles para todos los tipos primitivos, objetos y matrices (ya sean primitivas u objetos).
El orden de los parámetros, dentro de la aserción, es el valor esperado seguido del valor real; opcionalmente, el primer parámetro puede ser un mensaje de cadena que representa la salida del mensaje de la condición evaluada.
Solo hay uno ligeramente diferente en cómo se definen las afirmaciones assertThat , pero lo cubriremos más adelante.
Comencemos con assertEquals uno.
3.1. asertEquals
La aserción assertEquals verifica que los valores esperados y reales son iguales:
@Test public void whenAssertingEquality_thenEqual() { String expected = "Baeldung"; String actual = "Baeldung"; assertEquals(expected, actual); }
También es posible especificar un mensaje para mostrar cuando falla la aserción:
assertEquals("failure - strings are not equal", expected, actual);
3.2. assertArrayEquals
Si queremos afirmar que dos matrices son iguales, podemos usar assertArrayEquals:
@Test public void whenAssertingArraysEquality_thenEqual() { char[] expected = {'J','u','n','i','t'}; char[] actual = "Junit".toCharArray(); assertArrayEquals(expected, actual); }
Si ambas matrices son nulas , la aserción las considerará iguales:
@Test public void givenNullArrays_whenAssertingArraysEquality_thenEqual() { int[] expected = null; int[] actual = null; assertArrayEquals(expected, actual); }
3.3. assertNotNull y assertNull
Cuando queremos probar si un objeto es nulo , podemos usar la aserción assertNull :
@Test public void whenAssertingNull_thenTrue() { Object car = null; assertNull("The car should be null", car); }
De manera opuesta, si queremos afirmar que un objeto no debe ser nulo, podemos usar la aserción assertNotNull.
3.4. asertNotSame y assertSame
Con assertNotSame , es posible verificar si dos variables no se refieren al mismo objeto:
@Test public void whenAssertingNotSameObject_thenDifferent() { Object cat = new Object(); Object dog = new Object(); assertNotSame(cat, dog); }
De lo contrario, cuando queremos verificar que dos variables se refieren al mismo objeto, podemos usar la aserción assertSame .
3.5. afirmar verdadero y falso
En caso de querer verificar que una determinada condición es verdadera o falsa , podemos utilizar, respectivamente, la assertTrue afirmación o la assertFalse uno:
@Test public void whenAssertingConditions_thenVerified() { assertTrue("5 is greater then 4", 5 > 4); assertFalse("5 is not greater then 6", 5 > 6); }
3.6. fallar
La aserción fallida falla en una prueba y arroja un AssertionFailedError . Se puede usar para verificar que se lanza una excepción real o cuando queremos que una prueba falle durante su desarrollo.
Veamos cómo podemos usarlo en el primer escenario:
@Test public void whenCheckingExceptionMessage_thenEqual() { try { methodThatShouldThrowException(); fail("Exception not thrown"); } catch (UnsupportedOperationException e) { assertEquals("Operation Not Supported", e.getMessage()); } }
3.7. afirmar que
La aserción Esa aserción es la única en JUnit 4 que tiene un orden inverso de los parámetros en comparación con las otras aserciones.
En este caso, la aserción tiene un mensaje de error opcional, el valor real y un objeto Matcher .
Veamos cómo podemos usar esta afirmación para verificar si una matriz contiene valores particulares:
@Test public void testAssertThatHasItems() { assertThat( Arrays.asList("Java", "Kotlin", "Scala"), hasItems("Java", "Kotlin")); }
Información adicional, sobre el poderoso uso de la aserción assertThat con el objeto Matcher , está disponible en Testing with Hamcrest.
4. Declaraciones de JUnit 5
JUnit 5 mantuvo muchos de los métodos de aserción de JUnit 4 mientras agrega algunos nuevos que aprovechan el soporte de Java 8.
También en esta versión de la biblioteca, las aserciones están disponibles para todos los tipos primitivos, objetos y matrices (ya sean primitivas u objetos).
The order of the parameters of the assertions changed, moving the output message parameter as the last parameter. Thanks to the support of Java 8, the output message can be a Supplier, allowing lazy evaluation of it.
Let's start reviewing the assertions available also in JUnit 4.
4.1. assertArrayEquals
The assertArrayEquals assertion verifies that the expected and the actual arrays are equals:
@Test public void whenAssertingArraysEquality_thenEqual() { char[] expected = { 'J', 'u', 'p', 'i', 't', 'e', 'r' }; char[] actual = "Jupiter".toCharArray(); assertArrayEquals(expected, actual, "Arrays should be equal"); }
If the arrays aren't equal, the message “Arrays should be equal” will be displayed as output.
4.2. assertEquals
In case we want to assert that two floats are equals, we can use the simple assertEquals assertion:
@Test public void whenAssertingEquality_thenEqual() { float square = 2 * 2; float rectangle = 2 * 2; assertEquals(square, rectangle); }
However, if we want to assert that the actual value differs by a predefined delta from the expected value, we can still use the assertEquals but we have to pass the delta value as the third parameter:
@Test public void whenAssertingEqualityWithDelta_thenEqual() { float square = 2 * 2; float rectangle = 3 * 2; float delta = 2; assertEquals(square, rectangle, delta); }
4.3. assertTrue and assertFalse
With the assertTrue assertion, it's possible to verify the supplied conditions are true:
@Test public void whenAssertingConditions_thenVerified() { assertTrue(5 > 4, "5 is greater the 4"); assertTrue(null == null, "null is equal to null"); }
Thanks to the support of the lambda expression, it's possible to supply a BooleanSupplier to the assertion instead of a boolean condition.
Let's see how we can assert the correctness of a BooleanSupplier using the assertFalse assertion:
@Test public void givenBooleanSupplier_whenAssertingCondition_thenVerified() { BooleanSupplier condition = () -> 5 > 6; assertFalse(condition, "5 is not greater then 6"); }
4.4. assertNull and assertNotNull
When we want to assert that an object is not null we can use the assertNotNull assertion:
@Test public void whenAssertingNotNull_thenTrue() { Object dog = new Object(); assertNotNull(dog, () -> "The dog should not be null"); }
In the opposite way, we can use the assertNull assertion to check if the actual is null:
@Test public void whenAssertingNull_thenTrue() { Object cat = null; assertNull(cat, () -> "The cat should be null"); }
In both cases, the failure message will be retrieved in a lazy way since it's a Supplier.
4.5. assertSame and assertNotSame
When we want to assert that the expected and the actual refer to the same Object, we must use the assertSame assertion:
@Test public void whenAssertingSameObject_thenSuccessfull() { String language = "Java"; Optional optional = Optional.of(language); assertSame(language, optional.get()); }
In the opposite way, we can use the assertNotSame one.
4.6. fail
The fail assertion fails a test with the provided failure message as well as the underlying cause. This can be useful to mark a test when it's development it's not completed:
@Test public void whenFailingATest_thenFailed() { // Test not completed fail("FAIL - test not completed"); }
4.7. assertAll
One of the new assertion introduced in JUnit 5 is assertAll.
This assertion allows the creation of grouped assertions, where all the assertions are executed and their failures are reported together. In details, this assertion accepts a heading, that will be included in the message string for the MultipleFailureError, and a Stream of Executable.
Let's define a grouped assertion:
@Test public void givenMultipleAssertion_whenAssertingAll_thenOK() { assertAll( "heading", () -> assertEquals(4, 2 * 2, "4 is 2 times 2"), () -> assertEquals("java", "JAVA".toLowerCase()), () -> assertEquals(null, null, "null is equal to null") ); }
The execution of a grouped assertion is interrupted only when one of the executables throws a blacklisted exception (OutOfMemoryError for example).
4.8. assertIterableEquals
The assertIterableEquals asserts that the expected and the actual iterables are deeply equal.
In order to be equal, both iterable must return equal elements in the same order and it isn't required that the two iterables are of the same type in order to be equal.
With this consideration, let's see how we can assert that two lists of different types (LinkedList and ArrayList for example) are equal:
@Test public void givenTwoLists_whenAssertingIterables_thenEquals() { Iterable al = new ArrayList(asList("Java", "Junit", "Test")); Iterable ll = new LinkedList(asList("Java", "Junit", "Test")); assertIterableEquals(al, ll); }
In the same way of the assertArrayEquals, if both iterables are null, they are considered equal.
4.9. assertLinesMatch
The assertLinesMatch asserts that the expected list of String matches the actual list.
This method differs from the assertEquals and assertIterableEquals since, for each pair of expected and actual lines, it performs this algorithm:
- check if the expected line is equal to the actual one. If yes it continues with the next pair
- treat the expected line as a regular expression and performs a check with the String.matches() method. If yes it continues with the next pair
- check if the expected line is a fast-forward marker. If yes apply fast-forward and repeat the algorithm from the step 1
Let's see how we can use this assertion to assert that two lists of String have matching lines:
@Test public void whenAssertingEqualityListOfStrings_thenEqual() { List expected = asList("Java", "\\d+", "JUnit"); List actual = asList("Java", "11", "JUnit"); assertLinesMatch(expected, actual); }
4.10. assertNotEquals
Complementary to the assertEquals, the assertNotEquals assertion asserts that the expected and the actual values aren't equal:
@Test public void whenAssertingEquality_thenNotEqual() { Integer value = 5; // result of an algorithm assertNotEquals(0, value, "The result cannot be 0"); }
If both are null, the assertion fails.
4.11. assertThrows
In order to increase simplicity and readability, the new assertThrows assertion allows us a clear and a simple way to assert if an executable throws the specified exception type.
Let's see how we can assert a thrown exception:
@Test void whenAssertingException_thenThrown() { Throwable exception = assertThrows( IllegalArgumentException.class, () -> { throw new IllegalArgumentException("Exception message"); } ); assertEquals("Exception message", exception.getMessage()); }
The assertion will fail if no exception is thrown, or if an exception of a different type is thrown.
4.12. assertTimeout and assertTimeoutPreemptively
In case we want to assert that the execution of a supplied Executable ends before a given Timeout, we can use the assertTimeout assertion:
@Test public void whenAssertingTimeout_thenNotExceeded() { assertTimeout( ofSeconds(2), () -> { // code that requires less then 2 minutes to execute Thread.sleep(1000); } ); }
However, with the assertTimeout assertion, the supplied executable will be executed in the same thread of the calling code. Consequently, execution of the supplier won't be preemptively aborted if the timeout is exceeded.
In case we want to be sure that execution of the executable will be aborted once it exceeds the timeout, we can use the assertTimeoutPreemptively assertion.
Ambas aserciones pueden aceptar, en lugar de un ejecutable, un ThrowingSupplier , que representa cualquier bloque genérico de código que devuelve un objeto y que potencialmente puede lanzar un Throwing.
5. Conclusión
En este tutorial, cubrimos todas las afirmaciones disponibles tanto en JUnit 4 como en JUnit 5.
Destacamos brevemente las mejoras realizadas en JUnit 5, con la introducción de nuevas aserciones y el soporte de lambdas.
Como siempre, el código fuente completo de este artículo está disponible en GitHub.