Matchers predefinidos
⏱ Dedicación recomendada: 0 minutos
Esto considera el contenido visible y relevante, e ignora texto colapsado o marcado como opcional.
r8vnhill/assertions-kt
Los matchers son el corazón de las pruebas expresivas en Kotest. Permiten escribir afirmaciones legibles y declarativas, enfocadas más en el qué esperamos del valor evaluado que en el cómo lo verificamos. Gracias a su sintaxis fluida, los matchers ayudan a que los tests se lean como descripciones naturales del comportamiento esperado.
En esta lección conocerás cómo usar los matchers predefinidos que ofrece Kotest: desde validaciones simples como comprobar igualdad o nulidad, hasta expresiones más sofisticadas encadenadas. También verás cómo esta forma de testear no solo mejora la claridad del código, sino que facilita la detección de errores y hace que tus pruebas se comuniquen mejor con otras personas.
Kotest incluye cientos de matchers especializados para trabajar con tipos como listas, cadenas, fechas, JSON, e incluso excepciones. Aprender a dominarlos es clave para escribir tests más concisos, expresivos y mantenibles.
Ejemplos Comunes de Matchers en Kotest
Igualdad y Desigualdad
El matcher shouldBe
en Kotest verifica si dos valores son iguales estructuralmente, es decir, si a == b
. Esto significa que no compara identidad de objetos (===
), sino su contenido lógico tal como lo define el método equals()
.
💡 Ejemplo contextualizado
Imaginemos que estás desarrollando una biblioteca para manejar configuraciones de usuario. Tienes una función que devuelve una configuración por defecto, y quieres asegurarte de que su contenido sea el correcto, sin importar si es la misma instancia:
- Código esencial
- Código completo
defaultConfig shouldBe UserConfig()
package com.github.username.config
import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.shouldBe
class UserConfigTest : FreeSpec({
"Given no custom configuration" - {
"When using the default configuration" - {
"Then it should match the expected default values" {
defaultConfig shouldBe UserConfig()
}
}
}
})
package com.github.username.config
class UserConfig(
val theme: String = "light",
val notificationsEnabled: Boolean = true
) {
override fun toString(): String =
"UserConfig(theme='$theme', notificationsEnabled=$notificationsEnabled)"
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is UserConfig) return false
if (theme != other.theme) return false
if (notificationsEnabled != other.notificationsEnabled) return false
return true
}
override fun hashCode(): Int {
var result = theme.hashCode()
result = 31 * result + notificationsEnabled.hashCode()
return result
}
}
val defaultConfig get() = UserConfig()
El matcher shouldBe
compara dos valores usando ==
, es decir, verifica que sean estructuralmente iguales según su método equals()
.
En este caso, asegura que defaultConfig
tenga el mismo contenido que una instancia por defecto de UserConfig
. No importa si son la misma instancia, sino que representen la misma configuración lógica.
Si hay alguna diferencia en sus propiedades (como theme
o notificationsEnabled
), la prueba fallará indicando las discrepancias.
❌ Desigualdad
Si deseas comprobar que dos valores no son iguales estructuralmente, puedes usar el matcher shouldNotBe
:
- Código esencial
- Código completo
val testConfig = UserConfig(
theme = "dark",
notificationsEnabled = false
)
testConfig shouldNotBe defaultConfig
package com.github.username.config
import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
class UserConfigTest : FreeSpec({
"Given no custom configuration" - {
"When using the default configuration" - {
"Then it should match the expected default values" {
defaultConfig shouldBe UserConfig()
}
}
}
"Given a custom configuration" - {
"When compared to the default configuration" - {
"Then it should not match" {
val testConfig = UserConfig(theme = "dark", notificationsEnabled = false)
testConfig shouldNotBe defaultConfig
}
}
}
})
El matcher shouldNotBe
verifica que dos valores no sean iguales estructuralmente, es decir, que a != b
según su implementación de equals()
. La prueba falla si los objetos comparados tienen exactamente el mismo contenido.
Usa shouldBeSameInstanceAs
para comprobar que dos referencias apuntan al mismo objeto en memoria (===
), y shouldNotBeSameInstanceAs
para lo contrario (!==
). A diferencia de shouldBe
, estos matchers no comparan el contenido, sino la identidad de instancia.
a shouldBeSameInstanceAs b
a shouldNotBeSameInstanceAs c
- No Nulo: Asegura que un valor no es nulo.
result.shouldNotBeNull()
- Contenido en Cadenas: Verifica que una cadena contiene otra.
string.shouldContain("world")
- Empieza con: Verifica que una cadena comienza con un prefijo específico.
string.shouldStartWith("Hello")
Encadenar Matchers
Una de las ventajas de Kotest es que puedes encadenar matchers para hacer varias verificaciones en una sola expresión, mejorando la legibilidad del código.
Por ejemplo:
result.shouldNotBeNull()
.shouldBeGreaterThan(10)
.shouldBeLessThan(50)
En este ejemplo, se verifica que result
:
- No es nulo.
- Es mayor que 10.
- Es menor que 50.
Otro ejemplo con cadenas:
string.shouldStartWith("Hello")
.shouldContain("world")
.shouldEndWith("!")
Aquí, se verifica que string
:
- Comienza con "Hello".
- Contiene "world".
- Termina con "!".
El encadenamiento de matchers permite que las pruebas sean más expresivas y fáciles de seguir, eliminando la necesidad de múltiples sentencias assert
y mejorando la fluidez de la validación.
Beneficios y limitaciones
Beneficios
- Legibilidad mejorada: Los matchers en Kotest ofrecen una sintaxis más expresiva y natural que las aserciones tradicionales (
assert
). Esto facilita que las pruebas sean más claras y fáciles de entender. - Encadenamiento: Los matchers permiten encadenar múltiples validaciones en una sola línea, lo que reduce la cantidad de código repetitivo y hace que las pruebas sean más fluidas.
- Errores más detallados: Cuando una prueba falla, los matchers suelen generar mensajes de error más específicos y detallados que las aserciones comunes, lo que facilita la depuración.
- Gran variedad de validaciones: Kotest proporciona una amplia gama de matchers (más de 350) que cubren validaciones específicas, lo que permite que las pruebas se adapten mejor a diferentes tipos de datos y contextos.
- Consistencia: El uso de matchers permite un enfoque más consistente y estandarizado en las pruebas, especialmente cuando se utilizan en proyectos grandes o con varios colaboradores.
Limitaciones
- Sobrecarga de dependencias: Utilizar matchers avanzados puede requerir la inclusión de dependencias adicionales (como
kotest-assertions-json
okotest-assertions-core
), lo que puede aumentar el tamaño del proyecto innecesariamente en casos simples. - Curva de aprendizaje: Aunque son más expresivos, los matchers requieren que lxs desarrolladorxs aprendan una nueva API en lugar de utilizar aserciones tradicionales que pueden ser más familiares.
¿Qué hemos visto?
En esta sección hemos explorado los matchers en Kotest, aprendiendo a verificar condiciones como igualdad, no nulidad, y contenido de cadenas de manera expresiva. Vimos ejemplos comunes de matchers como shouldBe
, shouldNotBe
, y cómo encadenarlos para hacer las pruebas más legibles y eficientes.
Esta forma de definir expectativas mejora la claridad, consistencia y fluidez de las pruebas.
Bibliografías Recomendadas
- 🌐 "Core Matchers | Kotest." Accedido: 26 de septiembre de 2024. [En línea]. Disponible en: https://kotest.io/docs/assertions/core-matchers.html