Ir al contenido principal
Versión: 5.3.x

Lanzamiento 4.1

[Traducción Beta No Oficial]

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

El equipo de Kotest se complace en anunciar el lanzamiento de Kotest 4.1.0. Esta versión menor está repleta de novedades, incluido el primer lanzamiento público del plugin para IntelliJ. En esta entrada del blog cubriremos algunas características y cambios destacados, pero para la lista completa consulta el changelog.

Plugin de Kotest

Empecemos con la noticia más emocionante. Como parte del ciclo de lanzamiento 4.1.0, hemos publicado la primera versión pública del plugin de Kotest para IntelliJ. El plugin está disponible en el repositorio de JetBrains, así que dirígete a Configuración -> Plugins y busca "kotest".

plugin image

Al tratarse del primer lanzamiento que usarán la mayoría de usuarios, es probable que aparezcan errores. Si encuentras algún problema, por favor crea un ticket aquí.

El plugin proporciona iconos de ejecución en el gutter para specs, pruebas de nivel superior y pruebas anidadas.

gutter_icon_picture

Además, el plugin incluye una ventana de herramientas que muestra la estructura de tus pruebas. Esta ventana describe el archivo de pruebas seleccionado, incluyendo cualquier spec definida en ese archivo y las pruebas contenidas dentro de esas specs. El árbol reflejará la estructura de tus pruebas para facilitar la navegación.

La ventana incluirá métodos de callback del ciclo de vida (como before/after test) si están definidos, así como fábricas de pruebas incluidas.

Al hacer clic en una spec, prueba, inclusión o callback, navegarás directamente a ese elemento en el editor de código.

test_explorer_tests

Para conocer todas las funcionalidades del plugin, consulta el readme.

Nota: Para dar soporte a este plugin, hemos eliminado el código interno que hacía creer a IntelliJ que las specs de Kotest eran pruebas de Junit. Esto significa que, sin el plugin instalado, ya no verás el icono verde de reproducción en el nombre de la clase.

Eliminación de alias Kotlintest

En el lanzamiento 4.0 de Kotest, el proyecto cambió su nombre de Kotlintest. Para facilitar la migración, creamos alias desde los paquetes kotlintest hacia los paquetes kotest para importaciones comunes.

Con el lanzamiento de 4.1, estos alias se han eliminado.

Resaltado de diferencias al comparar data classes

Al comparar dos data classes, antes debías revisar manualmente los campos para identificar las diferencias. Ahora, la salida de error resaltará automáticamente las diferencias.

Por ejemplo, dada la siguiente data class:

data class Foo(val a: String, val b: Boolean, val c: Double)

Y al ejecutar esto:

val a = Foo("hello", true, 1.0)
val b = Foo("world", true, 1.3)
a shouldBe b

Obtendrás esta salida:

data class diff for Foo
Expected :Foo(a=world, b=true, c=1.3)
Actual :Foo(a=hello, b=true, c=1.0)
<Click to see difference>

org.opentest4j.AssertionFailedError: data class diff for Foo
├ a: expected:<"world"> but was:<"hello">
└ c: expected:<1.3> but was:<1.0>

Integración con Testcontainers

Testcontainers es una popular biblioteca Java que permite usar instancias ligeras y desechables de bases de datos, colas de mensajes, Elasticsearch y otros servicios. Ahora Kotest incluye un módulo que facilita su integración en el ciclo de vida de pruebas.

Añade el módulo kotest-extensions-testcontainers a tu build y registra un contenedor así:

val testStartable = SomeTestContainer()
listeners(testStartable.perTest())

Observa la función .perTest() que crea un listener para detener e iniciar el contenedor entre pruebas. Para un contenedor que solo se inicia y detiene una vez por spec, usa:

val testStartable = SomeTestContainer()
listeners(testStartable.perSpec())

Variantes 'x' para Specs

Los populares frameworks de JavaScript y RSpec en Ruby han popularizado el estilo de pruebas basado en describe / it. Kotest ha soportado esto desde la versión 1.0 con el DescribeSpec. Estos frameworks también ofrecen una forma sencilla de desactivar una prueba, reemplazando describe por xdescribe e it por xit. Kotest también incluye esta funcionalidad.

A partir de la versión 4.1, Kotest extiende esta misma funcionalidad a otros estilos. Por ejemplo, puedes desactivar un bloque given en BehaviorSpec usando xgiven, o un bloque context en FunSpec con xcontext, entre otros.

Ejemplo completo en estilo FunSpec:

class MyFunSpec : FunSpec({
xtest("a disabled test") {
// this test will not be invoked
}
xcontext("this context is disabled") {
test("and so this test is by extension") {
}
}
})

Consulta todos los detalles en la página de estilos.

Eliminación de prefijos en la salida de pruebas

Siguiendo con la sección anterior, al usar ciertos specs los nombres de las pruebas incluían prefijos como Describe: o Feature: en la salida.

Esto añadía ruido innecesario y, en retrospectiva, no debería haberse implementado. A partir de la 4.1 puedes desactivar estos prefijos de ámbito de prueba configurando includeTestScopePrefixes a false en tu configuración de proyecto.

Nota: En la versión 4.2.0 esta opción estará desactivada por defecto.

Timeouts a nivel de invocación

Kotest permite aplicar timeouts a tus pruebas mediante configuración en el caso de prueba.

test("some test").config(timeout = 3000.milliseconds) { }

Este timeout se aplica a todas las invocaciones de esa prueba. Si configuras más de una invocación, el timeout se comparte entre todas. Desde la 4.1 puedes aplicar un timeout a nivel de cada invocación individual.

test("some test").config(timeout = 3000.milliseconds,
invocationTimeout = 250.milliseconds,
invocations = 10) { }

Ejecución paralela de pruebas

Kotest ha permitido durante tiempo ejecutar specs en paralelo. A partir de la 4.1 puedes ejecutar casos de prueba individuales en paralelo. Sobrescribe el valor threads dentro de tu clase spec con un valor mayor que 1. Nota: Esta característica es experimental y solo aplica al modo de aislamiento de instancia única.

Todos los ámbitos son ahora corrutinas

Los casos de prueba hoja siempre han sido ámbitos de corrutina desde la versión 3.2 de Ko(tlin)Test. Esto permite usar launch directamente en el bloque de prueba sin necesidad de proveer un ámbito como GlobalScope o tu propia instancia de CoroutineScope.

test("some test") {
launch {
delay(100)
}
}

Anteriormente, los ámbitos padres en estilos que permiten anidamiento no eran ámbitos de corrutina. Esto ha cambiado en la 4.1.

Ahora puedes escribir pruebas como esta:

describe("some test") {
launch {
delay(100)
it("should do something") {
launch {
delay(100)
}
}
}
}

beforeProject y afterProject como funciones suspend

Otra característica que fue más un descuido que otra cosa: los callbacks beforeProject y afterProject en ProjectListener son ahora funciones suspendibles.

Aserciones suaves con receptor

Probablemente ya uses assertSoftly para permitir que una prueba finalice antes de lanzar todos los fallos. Ahora puedes hacer lo mismo con un receptor.

Por ejemplo, en lugar de escribir

val person = ...
assertSoftly {
person.name shouldBe "sam"
person.age shouldBe 99
person.city shouldBe "Chicago"
}

Ahora puedes hacer:

val person = ...
person.assertSoftly {
name shouldBe "sam"
age shouldBe 99
city shouldBe "Chicago"
}

Mejor información de reducción (shrinking)

Si usas el framework de pruebas basadas en propiedades, notarás la salida mejorada del proceso de reducción. Ahora incluye tanto la razón del fallo original (con los argumentos originales) como la razón del fallo reducido (con los argumentos reducidos).

Por ejemplo, dada una prueba tonta que verifica que cualquier string invertido es igual al string original:

checkAll<String> { a ->
a shouldBe a.reversed()
}

Esto será cierto para strings vacíos y de un solo carácter, pero falso para la mayoría de strings.

Property test failed for inputs

0) "!s:?XBy;pq?`$3V70cqoO$zlO&%bUwafP1nF73gMeyQ[RzehtY36"

Caused by org.opentest4j.AssertionFailedError: expected:<"63YthezR[QyeMg37Fn1PfawUb%&Olz$Ooqc07V3$`?qp;yBX?:s!"> but was:<"!s:?XBy;pq?`$3V70cqoO$zlO&%bUwafP1nF73gMeyQ[RzehtY36"> at
com.sksamuel.kotest.property.ForAll2Test$1$1$1.invokeSuspend(ForAll2Test.kt:19)
com.sksamuel.kotest.property.ForAll2Test$1$1$1.invoke(ForAll2Test.kt)
io.kotest.property.internal.ProptestKt$proptest$$inlined$forEach$lambda$1.invokeSuspend(proptest.kt:28)
io.kotest.property.internal.ProptestKt$proptest$$inlined$forEach$lambda$1.invoke(proptest.kt)

Attempting to shrink arg "!s:?XBy;pq?`$3V70cqoO$zlO&%bUwafP1nF73gMeyQ[RzehtY36"
Shrink #1: "!s:?XBy;pq?`$3V70cqoO$zlO&" fail
Shrink #2: "!s:?XBy;pq?`$" fail
Shrink #3: "!s:?XBy" fail
Shrink #4: "!s:?" fail
Shrink #5: "!s" fail
Shrink #6: "!" pass
Shrink #7: "as" fail
Shrink #8: "a" pass
Shrink #9: "s" pass
Shrink #10: "aa" pass
Shrink result (after 10 shrinks) => "as"

Caused by org.opentest4j.AssertionFailedError: expected:<"sa"> but was:<"as"> at
com.sksamuel.kotest.property.ForAll2Test$1$1$1.invokeSuspend(ForAll2Test.kt:19)
com.sksamuel.kotest.property.ForAll2Test$1$1$1.invoke(ForAll2Test.kt)
io.kotest.property.internal.ShrinkfnsKt$shrinkfn$1$invokeSuspend$$inlined$with$lambda$1.invokeSuspend(shrinkfns.kt:19)
io.kotest.property.internal.ShrinkfnsKt$shrinkfn$1$invokeSuspend$$inlined$with$lambda$1.invoke(shrinkfns.kt)

Listeners para pruebas basadas en propiedades

Las funciones de pruebas de propiedades forAll y checkAll aceptan un objeto PropTestConfig para configurar la prueba. Este objeto ahora incluye un campo listeners al que puedes asociar instancias de PropTestListener. Esto te permite ejecutar código de configuración/desmontaje antes y después de una prueba de propiedades, igual que haces con pruebas regulares.

Por ejemplo:

val listener = object : PropTestListener {
override suspend fun beforeTest() {
println("Startup")
}

override suspend fun afterTest() {
println("Shutdown")
}
}

val propConfig = PropTestConfig(listeners = listOf(listener))

checkAll<String, String>(10, propConfig) { a, b ->
a.length + b.length shouldBe (a + b).length
}

Agradecimientos

Un enorme agradecimiento a todos los que han contribuido a esta versión.

AJ Alt, Albert Attard, Amy, Ashish Kumar Joy, ataronet, Attila Domokos, bbaldino, bright_spark, Caroline Ribeiro, Christian Nedregård, crazyk2, George Wilkins, Harry JinHyeok Kang, James Pittendreigh, Leonardo Colman Lopes, Lyall Jonathan Di Trapani, Martin Nonnenmacher, Maxime Suret, mwfpope, Nikita Klimenko, Nimamoh, Octogonapus, Paul, Robert Macaulay, Robert Stoll, Ron Gebauer, Sebastian Schuberth, Sergei Bulgakov, sharmabhawna, sksamuel, Steffen Rehberg