Matchers personalizados en AssertJ
⏱ Dedicación recomendada: 0 minutos
Esto considera el contenido visible y relevante, e ignora texto colapsado o marcado como opcional.
r8vnhill/java-dibs
Existen dos formas de crear matchers personalizados en AssertJ. La primera es crear un objeto de tipo Condition
y la segunda es extender la clase AbstractAssert
.
Creación de un matcher con Condition
Para crear un matcher que verifique si un número es par, utilizamos la clase Condition
de AssertJ:
- Código esencial
- Código completo
public static Condition<Integer> even =
new Condition<>(value -> value % 2 == 0, "even");
package cl.ravenhill.matchers.assertj;
import org.assertj.core.api.Condition;
public class EvenConditionMatcher {
public static Condition<Integer> even =
new Condition<>(value -> value % 2 == 0, "even");
}
Creamos un objeto Condition
que verifica si un número es par. El constructor de Condition
recibe una expresión lambda que evalúa la condición y un mensaje de error en caso de fallo.
Uso del matcher en pruebas
- Código esencial
- Código completo
assertThat(2).is(even);
assertThrows(AssertionError.class, () -> assertThat(2).isNot(even));
assertThat(3).isNot(even);
assertThrows(AssertionError.class, () -> assertThat(3).is(even));
package cl.ravenhill.matchers.assertj;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static cl.ravenhill.matchers.assertj.EvenConditionMatcher.even;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class EvenConditionMatcherTest {
@DisplayName(
"Given an even number, " +
"when checking if it is even, " +
"then it should pass"
)
@Test
void testEvenNumberIsEven() {
assertThat(2).is(even);
}
@DisplayName(
"Given an even number, " +
"when checking if it is not even, " +
"then it should fail"
)
@Test
void testEvenNumberIsNotEven() {
assertThrows(AssertionError.class, () -> assertThat(2).isNot(even));
}
@DisplayName(
"Given an odd number, " +
"when checking if it is not even, " +
"then it should pass"
)
@Test
void testOddNumberIsNotEven() {
assertThat(3).isNot(even);
}
@DisplayName(
"Given an odd number, " +
"when checking if it is even, " +
"then it should fail"
)
@Test
void testOddNumberIsEven() {
assertThrows(AssertionError.class, () -> assertThat(3).is(even));
}
}
- En el código de prueba, utilizamos el matcher
even
para verificar si un número es par. - La aserción
assertThat(2).is(even)
verifica si el número 2 es par, mientras queassertThat(3).isNot(even)
verifica si el número 3 no es par.
Creación de un Matcher con AbstractAssert
Para crear un matcher personalizado en AssertJ, podemos extender la clase AbstractAssert
. Este enfoque permite crear verificaciones específicas con una sintaxis fluida y fácilmente integrable en las pruebas. En este ejemplo, crearemos un matcher para verificar si un número es par.
- Código esencial
- Código completo
public final class EvenNumberAssert
extends AbstractAssert<EvenNumberAssert, Integer> {
public EvenNumberAssert(Integer actual) {
super(actual, EvenNumberAssert.class);
}
public static @NotNull EvenNumberAssert assertThat(Integer actual) {
return new EvenNumberAssert(actual);
}
public EvenNumberAssert isEven() {
isNotNull();
if (actual % 2 != 0) {
failWithMessage(
"Expected <%s> to be even, but it was not.",
actual
);
}
return this;
}
}
package cl.ravenhill.matchers.assertj;
import org.assertj.core.api.AbstractAssert;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
public final class EvenNumberAssert
extends AbstractAssert<EvenNumberAssert, Integer> {
public EvenNumberAssert(Integer actual) {
super(actual, EvenNumberAssert.class);
}
@Contract("_ -> new")
public static @NotNull EvenNumberAssert assertThat(Integer actual) {
return new EvenNumberAssert(actual);
}
public EvenNumberAssert isEven() {
isNotNull();
if (actual % 2 != 0) {
failWithMessage(
"Expected <%s> to be even, but it was not.",
actual
);
}
return this;
}
}
- La clase
EvenNumberAssert
hereda deAbstractAssert
, lo que permite definir métodos de aserción personalizados. - El método
assertThat()
actúa como un punto de entrada estático, creando una instancia deEvenNumberAssert
que facilita su uso en pruebas. - El método
isEven()
comprueba si el número actual (actual
) es par; si no lo es, lanza un mensaje de error personalizado para hacer la falla de la prueba más comprensible.
Uso del Matcher en Pruebas
Para probar el matcher personalizado isEven
, podemos crear dos casos de prueba: uno donde la verificación de paridad pase y otro donde falle.
- Código esencial
- Código completo
assertThat(2).isEven();
assertThrows(AssertionError.class, () -> assertThat(3).isEven());
package cl.ravenhill.matchers.assertj;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static cl.ravenhill.matchers.assertj.EvenNumberAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class EvenNumberAssertTest {
@DisplayName(
"Given an even number, " +
"when checking if it is even, " +
"then it should pass"
)
@Test
void testEvenNumber() {
assertThat(2).isEven();
}
@DisplayName(
"Given an odd number, " +
"when checking if it is even, " +
"then it should fail with a custom error message"
)
@Test
void testOddNumber() {
assertThrows(AssertionError.class, () -> assertThat(3).isEven());
}
}
Resumen comparativo
Característica | AssertJ | Kotest |
---|---|---|
Definición de matchers personalizados | En AssertJ, se pueden crear matchers personalizados extendiendo AbstractAssert o utilizando Condition . Esto permite realizar validaciones complejas y personalizadas mediante clases que heredan funcionalidad base. | En Kotest, los matchers personalizados se definen implementando la interfaz Matcher<T> , lo que simplifica la creación de validaciones específicas sin necesidad de crear clases enteras. |
Sintaxis fluida | AssertJ ofrece una sintaxis fluida y encadenable, permitiendo múltiples validaciones en una sola línea (isEven().isGreaterThan() ). | Kotest también ofrece una sintaxis fluida con operadores como should y shouldNot , lo que facilita la creación de pruebas expresivas y legibles. |
Compatibilidad | Está diseñado principalmente para Java, lo que lo hace ideal para proyectos que utilizan el ecosistema Java. | Kotest está optimizado para Kotlin, aprovechando las características de este lenguaje, como coroutines y expresiones lambda. |
Flexibilidad para crear matchers | En AssertJ, los matchers personalizados requieren más configuración, especialmente si se utiliza AbstractAssert . Sin embargo, este enfoque ofrece más control en casos complejos. | Crear un matcher en Kotest es más sencillo y directo. Basta con implementar Matcher<T> y definir las condiciones. |
Soporte para asincronía | AssertJ no tiene soporte nativo para asincronía, lo que puede ser una limitación en proyectos que dependen de operaciones concurrentes o asíncronas. | Kotest soporta asincronía nativamente, integrando coroutines y facilitando las pruebas de código que utiliza operaciones asíncronas. |
Uso en JVM y Kotlin | Aunque AssertJ se puede utilizar en proyectos Kotlin, no está diseñado específicamente para este lenguaje, lo que puede limitar la integración perfecta con características avanzadas de Kotlin. | Kotest está diseñado específicamente para Kotlin y la JVM, lo que garantiza una integración más fluida y un mejor uso de las características del lenguaje. |
Beneficios y limitaciones
Beneficios
- Sintaxis fluida y encadenable: AssertJ ofrece una sintaxis fluida que permite encadenar múltiples validaciones en una sola línea, lo que facilita la lectura y mejora la expresividad de las pruebas.
- Flexibilidad en la creación de matchers: Con AssertJ, los matchers personalizados se pueden definir usando
Condition
para situaciones simples o extendiendoAbstractAssert
para validaciones más complejas. Esto ofrece flexibilidad dependiendo del nivel de personalización necesario. - Amplia compatibilidad con Java: AssertJ está bien integrado en el ecosistema de Java, haciéndolo una opción ideal para proyectos Java que requieren una biblioteca rica en funcionalidad para pruebas.
- Personalización avanzada: Al extender
AbstractAssert
, se puede obtener un control total sobre cómo se manejan las validaciones y los mensajes de error, lo que permite personalizar cada detalle del matcher.
Limitaciones
- Complejidad en la creación de matchers: Comparado con Kotest, crear matchers personalizados en AssertJ puede ser más complejo y requiere más configuración, especialmente si se utiliza
AbstractAssert
. - Falta de soporte para asincronía: AssertJ no tiene soporte nativo para operaciones asíncronas, lo que puede ser un inconveniente en proyectos que dependen de procesos concurrentes o asíncronos.
- Menor integración con Kotlin: Aunque AssertJ puede ser utilizado en proyectos Kotlin, no está optimizado para este lenguaje, lo que significa que puede no aprovechar completamente las características avanzadas de Kotlin, como
coroutines
o lambdas. - Mayor carga de configuración: Mientras que Kotest permite crear matchers personalizados de manera sencilla implementando una interfaz, AssertJ requiere la creación de clases completas y métodos adicionales, lo que puede aumentar la carga de configuración para casos complejos.
¿Qué aprendimos?
En esta lección, hemos comparado la creación de matchers personalizados entre AssertJ y Kotest, observando las diferencias clave en cómo se definen y utilizan estos matchers en cada marco de pruebas.
Puntos clave
- Matchers Personalizados en AssertJ:
- AssertJ permite crear matchers personalizados de dos formas: mediante
Condition
o extendiendo la claseAbstractAssert
. Esto ofrece flexibilidad para validar condiciones complejas. - La creación de matchers mediante
AbstractAssert
proporciona un alto grado de control sobre las validaciones y los mensajes de error, pero también implica mayor complejidad y configuración.
- AssertJ permite crear matchers personalizados de dos formas: mediante
- Matchers Personalizados en Kotest:
- Kotest facilita la creación de matchers personalizados mediante la implementación de la interfaz
Matcher<T>
. Este enfoque es más sencillo y directo, ideal para validaciones que no requieren una estructura compleja. - Kotest está diseñado para Kotlin, lo que permite aprovechar sus características avanzadas, como las
coroutines
, para soportar asincronía nativa en las pruebas.
- Kotest facilita la creación de matchers personalizados mediante la implementación de la interfaz
- Diferencias Notables:
- Sintaxis: Ambos frameworks ofrecen una sintaxis fluida y encadenable, aunque AssertJ destaca por su enfoque más Java y Kotest por su integración nativa con Kotlin.
- Asincronía: Kotest tiene una ventaja en proyectos que requieren pruebas de código asíncrono, ya que soporta nativamente
coroutines
. AssertJ, por otro lado, no tiene soporte nativo para asincronía. - Carga de configuración: AssertJ puede requerir más configuración al crear matchers complejos, mientras que Kotest simplifica el proceso mediante la interfaz
Matcher<T>
.
Ambos frameworks son útiles para la creación de matchers personalizados, pero la elección depende del lenguaje y las necesidades del proyecto. AssertJ es ideal para proyectos en Java que requieren un alto control y flexibilidad, mientras que Kotest es más adecuado para proyectos Kotlin que buscan simplicidad, soporte para asincronía y una integración más fluida con el lenguaje.
Bibliografías Recomendadas
- 🌐 "AssertJ - fluent assertions java library." Accedido: 4 de octubre de 2024. [En línea]. Disponible en: https://assertj.github.io/doc/