Skip to main content

Aserciones

⏱ Dedicación recomendada: 0 minutos
Esto considera el contenido visible y relevante, e ignora texto colapsado o marcado como opcional.


r8vnhill/assertions-kt

Be lazy...

Puedes ejecutar el siguiente comando para crear el módulo

./gradlew setupAssertionsModule

Mientras se crean los archivos necesarios, puedes leer el código para saber qué está pasando.

import tasks.ModuleSetupTask

tasks.register<ModuleSetupTask>("setupAssertionsModule") {
description = "Creates the base module and files for the assertions lesson"
module.set("assertions")

doLast {
createFiles(
"calculator",
test to "CalculatorTest.kt",
main to "Calculator.kt",
)
createFiles(
"assertions",
test to "assertTrue.kt",
test to "assertIsOrdered.kt",
)
}
}

Preocúpate de que el plugin assertions esté aplicado en el archivo build.gradle.kts de tu proyecto.

./gradlew setupAssertionsModule

Preocúpate de que el nuevo módulo esté incluido en el archivo settings.gradle.kts.

En esta lección aprenderás cómo implementar aserciones manuales y personalizadas, entendiendo cómo se comportan y cómo integrarlas con frameworks como Kotest. Esto sienta las bases para usar matchers más expresivos en lecciones futuras.

🧪 ¿Por qué necesitamos aserciones?

Al desarrollar bibliotecas o aplicaciones, una parte esencial del proceso es verificar que nuestro código haga lo que esperamos. Las aserciones son una de las herramientas más directas para lograr esto: son expresiones booleanas que validan condiciones críticas en tiempo de ejecución. Si la condición es verdadera, el programa continúa. Si es falsa, se lanza una excepción que interrumpe la ejecución y señala que algo se desvió del comportamiento esperado.

Esto convierte a las aserciones en una técnica valiosa dentro del testing funcional: permiten validar los invariantes del sistema, detectar errores al instante y reducir el tiempo necesario para depurar o corregir fallos.

✅ Beneficios clave del uso de aserciones

1. Detección temprana de errores

Las aserciones permiten capturar errores pequeños y sutiles que podrían pasar desapercibidos con otros métodos. Esto mejora la robustez del sistema y reduce el riesgo de propagar fallos a etapas más avanzadas del desarrollo.

2. Falla inmediata = feedback inmediato

En especial con aserciones duras (hard assertions), la ejecución se detiene justo en el punto donde algo falla. Esto facilita un ciclo de retroalimentación rápido: no se continúa evaluando condiciones irrelevantes una vez que algo crítico falla, permitiendo al equipo enfocarse directamente en el problema.

3. Verificación de garantías internas

Las aserciones también sirven como contratos internos dentro del código, ayudando a documentar y reforzar que ciertos bloques de lógica deben producir resultados correctos. Esto fortalece la autoconfianza del software al garantizar que sus partes clave se comportan de forma fiable.

🔍 ¿Cómo se clasifican las aserciones?

Tipos de aserciones y assertSoftly

Las aserciones pueden clasificarse según su comportamiento al validar condiciones:

  • Aserciones simples: Verifican una única condición. Si falla, el test se interrumpe inmediatamente. Útiles para validar entradas, salidas o invariantes simples.

  • Aserciones compuestas: Agrupan varias condiciones en una misma evaluación. Todas deben cumplirse para que la prueba pase, permitiendo validar estructuras más complejas o múltiples propiedades al mismo tiempo.

  • Aserciones suaves (soft assertions): No interrumpen la ejecución tras el primer fallo. En lugar de eso, acumulan los errores y los reportan al final. Son ideales cuando se quiere tener una visión completa del estado del sistema, incluso si algunas partes fallan.

En Kotest, el mecanismo para realizar aserciones suaves es assertSoftly, que agrupa varias verificaciones sin detener la ejecución ante la primera falla:

assertSoftly {
person.name shouldBe "Alice"
person.age shouldBe 30
person.email shouldContain "@"
}

Si una o más condiciones fallan, el framework mostrará todas las fallas juntas, facilitando el diagnóstico de errores sin necesidad de ejecutar múltiples veces.

Nota didáctica

Esta lección tiene fines didácticos y busca ilustrar cómo funcionan las aserciones.
En la práctica, en lugar de implementar nuestras propias aserciones, utilizamos matchers, que permiten escribir pruebas más expresivas y estructuradas.
En lecciones futuras, exploraremos el uso de matchers y cómo facilitan la validación en los tests.

📌 Implementación manual

Por ejemplo, consideremos una prueba escrita manualmente:

"Given a calculator" - {
"when adding 1 and 2" - {
"should return 3" {
Calculator.add(1, 2).takeIf { it == 3 }
?: throw AssertionError("1 + 2 should be 3")
}
}
}
¿Qué acabamos de hacer?

En este código, verificamos si Calculator.add(1, 2) es igual a 3. Si no lo es, lanzamos una excepción AssertionError con un mensaje descriptivo; de lo contrario, imprimimos "Test passed".

Supongamos ahora que tenemos la siguiente implementación errónea de Calculator:

assert/src/main/kotlin/com/github/username/calculator/Calculator.kt
package com.github.username.calculator

object Calculator {
fun add(a: Int, b: Int): Int = a - b
}

En este caso, si ejecutamos el test obtendremos la siguiente salida:

java.lang.AssertionError: 1 + 2 should be 3
at com.github.username.assertions.CalculatorTest$1$1.invokeSuspend(CalculatorTest.kt:8)
at com.github.username.assertions.CalculatorTest$1$1.invoke(CalculatorTest.kt)
at com.github.username.assertions.CalculatorTest$1$1.invoke(CalculatorTest.kt)
at io.kotest.core.spec.style.scopes.FreeSpecRootScope$invoke$1.invokeSuspend(FreeSpecRootScope.kt:26)
...
Caused by: java.lang.AssertionError: 1 + 2 should be 3
at com.github.username.assertions.CalculatorTest$1$1.invokeSuspend(CalculatorTest.kt:8)
at com.github.username.assertions.CalculatorTest$1$1.invoke(CalculatorTest.kt)
at com.github.username.assertions.CalculatorTest$1$1.invoke(CalculatorTest.kt)
at io.kotest.core.spec.style.scopes.FreeSpecRootScope$invoke$1.invokeSuspend(FreeSpecRootScope.kt:26)
...

Sin embargo, podemos simplificar y mejorar este test utilizando las aserciones que ofrece el framework:

Calculator.add(1, 2).takeIf { it == 3 }
?: fail("1 + 2 should be 3")
¿Qué acabamos de hacer?

Aquí, en lugar de lanzar una excepción manualmente, utilizamos la función fail proporcionada por el framework, que marca la prueba como fallida y proporciona el mensaje de error correspondiente. Esto también ayudará al framework a generar un informe detallado de la prueba.

🛠️ Implementación de Aserciones Personalizadas

Supongamos ahora que queremos implementar nuestras propias funciones de aserción personalizadas para simplificar la escritura de pruebas. Por ejemplo, podemos crear una función assertTrue que verifique si una condición es verdadera y lance una excepción si no lo es.

assert/src/test/kotlin/com/github/username/assertions/assertTrue.kt
package com.github.username.assertions

import io.kotest.assertions.fail

fun assertTrue(
condition: Boolean,
message: String = "Condition is not true"
) {
if (!condition) {
fail(message)
}
}
¿Qué acabamos de hacer?

La función assertTrue toma dos parámetros:

  • condition: la condición que se debe cumplir.
  • message: el mensaje de error que se mostrará si la condición no se cumple.

Si la condición no se cumple, se lanza una excepción AssertionError con el mensaje especificado.

🔥 Ejemplo de uso en tu prueba

assertTrue(Calculator.add(1, 2) == 3, "1 + 2 should be 3")
¿Qué acabamos de hacer?

En este ejemplo, utilizamos la función assertTrue para verificar si Calculator.add(1, 2) es igual a 3. Si la condición no se cumple, se lanza una excepción con el mensaje "1 + 2 should be 3".

⚖️ Beneficios y limitaciones

Beneficios

  • Claridad y Reusabilidad: Las aserciones personalizadas mejoran la legibilidad del código de pruebas y permiten reutilizar las mismas aserciones en diferentes casos, reduciendo duplicaciones.
  • Mensajes de error personalizados: Permite proporcionar mensajes de error más claros y específicos, lo que facilita la identificación de fallos en las pruebas.
  • Abstracción de complejidad: Las aserciones personalizadas ocultan detalles complejos de validación, dejando las pruebas más concisas y fáciles de mantener.
  • Integración con frameworks: Al crear aserciones personalizadas, se puede integrar fácilmente con frameworks de testing como Kotest, aprovechando su capacidad de generar informes detallados.

Limitaciones

  • Mayor mantenimiento: Si las aserciones personalizadas no están bien diseñadas o documentadas, pueden ser difíciles de mantener y propensas a errores si los requisitos cambian.
  • Sobrecarga inicial: Crear aserciones personalizadas desde cero puede llevar más tiempo y esfuerzo inicialmente, comparado con el uso de aserciones predefinidas de los frameworks.

🧠 Buenas prácticas

tip
  • Reutiliza funciones de aserción en varios tests para evitar duplicación.
  • Incluye siempre mensajes de error descriptivos.
  • Integra tus aserciones con el sistema del framework (como fail() en Kotest) para aprovechar sus reportes.

🔢 Ejercicio: Verificar si una lista está ordenada

Ejercicio

Supón que estás escribiendo una biblioteca que ordena rankings, y quieres asegurarte de que los resultados se ordenan correctamente antes de exponerlos a otrxs usuaries.

Crea una aserción assertIsOrdered: (List<Int>, Boolean) -> Unit que verifique si una lista de enteros está ordenada de forma ascendente.

  • Si el segundo parámetro es true, la lista debe estar ordenada de forma estrictamente ascendente (sin elementos repetidos).
  • Si el segundo parámetro es false, la lista puede contener elementos repetidos.
Solución
fun assertIsOrdered(list: List<Int>, isStrictlyOrdered: Boolean) {
if (isStrictlyOrdered) {
for (i in 0..<list.size - 1) {
if (list[i] >= list[i + 1]) {
fail("List is not strictly ordered")
}
}
} else {
for (i in 0..<list.size - 1) {
if (list[i] > list[i + 1]) {
fail("List is not ordered")
}
}
}
}

🎯 Conclusiones

A lo largo de esta lección, exploramos el rol fundamental de las aserciones como mecanismo para validar el comportamiento del sistema en distintos puntos clave del código. Comenzamos con implementaciones manuales para comprender su funcionamiento esencial y luego avanzamos hacia la creación de funciones personalizadas que encapsulan lógica reusable.

Aunque hoy en día los frameworks modernos favorecen el uso de matchers más expresivos, comprender cómo funcionan las aserciones básicas nos permite:

  • Entender qué ocurre "bajo el capó" en una prueba fallida,
  • Diseñar validaciones adaptadas al dominio, y
  • Construir herramientas propias cuando las existentes no son suficientes.

🔑 Puntos clave

  1. Una aserción es una validación que detiene la prueba si una condición no se cumple.
  2. Es posible implementar aserciones manualmente, pero frameworks como Kotest ofrecen utilidades como fail() y assertSoftly para integrarse mejor con los reportes.
  3. Las funciones de aserción personalizadas aumentan la expresividad, reducen duplicación y ayudan a expresar reglas del dominio.
  4. Mensajes claros facilitan la depuración cuando una prueba falla.
  5. Preparar funciones propias sienta las bases para comprender herramientas más potentes como matchers, validadores o DSLs de testing.

🧰 ¿Qué nos llevamos?

Aprender a escribir aserciones manuales nos obliga a pensar con precisión sobre qué se quiere validar, cómo y por qué. En el contexto del desarrollo de bibliotecas, esto es clave: las pruebas dejan de ser simples chequeos y se convierten en garantías explícitas del contrato que estamos definiendo para quienes usarán nuestro código.

Este conocimiento también te prepara para transicionar a herramientas más declarativas, como los matchers, que exploraremos en próximas lecciones. Así, ganamos control y flexibilidad para construir pruebas que sean no solo correctas, sino también claras, mantenibles y alineadas con el propósito de la biblioteca.

📖 Referencias

🔥 Recomendadas

📰 Tamas Cser. (2024, septiembre 5). What is Assertion Testing? Ensuring Quality and Accuracy. https://www.functionize.com/automated-testing/assertion

🔹 Adicionales

🌐 ¿Cuáles son las prácticas recomendadas para usar aserciones en las pruebas? (s. f.). Recuperado 23 de marzo de 2025, de https://es.linkedin.com/advice/0/what-best-practices-using-assertions-testing-6uxje?lang=es