Por qué las pruebas E2E son su mejor amigo (y cómo hacerse amigo de ellas en Kotlin)
\n\nImagine: escribió un servicio genial en Kotlin, todo compiló, las pruebas unitarias están en verde, la revisión de código pasó. Lo despliega a producción, y... los usuarios se quejan de que el botón «Comprar» no funciona, aunque las pruebas unitarias lo verificaban. ¿Le suena familiar? Bienvenido al mundo donde las pruebas E2E de aplicaciones Kotlin no son un lujo, sino una necesidad.
\n\nLas pruebas de extremo a extremo (end-to-end) verifican todo el recorrido del usuario: desde el clic en un botón hasta la escritura en la base de datos y viceversa. En este artículo no solo hablaremos de teoría. Escribiremos pruebas reales para una aplicación Kotlin, analizaremos las herramientas y mostraremos cómo no volverse loco al configurar la infraestructura. Abróchese el cinturón, habrá código y café.
\n\nQué son las pruebas E2E y en qué se diferencian de otros tipos
\n\nAclaremos las cosas de inmediato. Las pruebas E2E (o integrales) verifican el sistema completo, tal como lo ve el usuario. A diferencia de:
\n\n- \n
- Pruebas unitarias — verifican un método/clase de forma aislada. \n
- Pruebas de integración — verifican la interacción de dos o tres componentes (por ejemplo, controlador + servicio). \n
- Pruebas UI — a menudo se confunden con E2E, pero una prueba UI puede no verificar el backend, solo la representación de la interfaz. \n
Las pruebas E2E de aplicaciones Kotlin son el ciclo completo: interfaz de usuario (si la hay), API, lógica de negocio, base de datos, servicios externos. Todo debe funcionar en conjunto como un reloj.
\n\nHerramientas para pruebas E2E en Kotlin
\n\nLa elección de la herramienta depende de lo que esté probando: una aplicación Android, un servicio web en Ktor o una aplicación de escritorio. Aquí están las mejores para un desarrollador Kotlin:
\n\n- \n
- Selenide + Kotlin — para interfaces web. Es un envoltorio sobre Selenium, pero con sintaxis concisa, ideal para Kotlin. \n
- Ktor Client + TestContainers — para backend. Levanta la infraestructura de prueba (BD, caché) en Docker y ejecuta solicitudes HTTP. \n
- Espresso + Compose Test — para Android. Herramientas nativas que vienen con el Android SDK. \n
- Cucumber + Kotlin — si se necesitan escenarios BDD en un lenguaje comprensible para el cliente. \n
Nos centraremos en los dos primeros escenarios, ya que son los más universales.
\n\nEscribimos una prueba E2E para una aplicación web en Kotlin (Selenide)
\n\nSupongamos que tenemos una tienda en línea simple en Spring Boot + Kotlin. Necesitamos verificar el escenario: el usuario ingresa al sitio, agrega un producto al carrito y realiza el pedido.
\n\nPrimero, agreguemos las dependencias en build.gradle.kts:
dependencies {\n testImplementation("com.codeborne:selenide:6.19.1")\n testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")\n testImplementation("io.github.bonigarcia:webdrivermanager:5.6.2")\n}\n\nAhora escribamos la prueba en sí. Observe: usamos funciones de extensión de Kotlin para que el código sea legible:
\n\nimport com.codeborne.selenide.Condition.visible\nimport com.codeborne.selenide.Selenide.`$`\nimport com.codeborne.selenide.Selenide.open\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\n\nclass ShopE2ETest {\n\n @BeforeEach\n fun setUp() {\n // Abrimos el navegador antes de cada prueba\n open("https://test-shop.example.com")\n }\n\n @Test\n fun `user can add product to cart and checkout`() {\n // Paso 1: Encontrar el producto y agregarlo al carrito\n `$`("[data-testid='product-1']").shouldBe(visible).click()\n `$`("[data-testid='add-to-cart']").click()\n\n // Paso 2: Verificar que el carrito no esté vacío\n `$`("[data-testid='cart-badge']").shouldHave(text("1"))\n\n // Paso 3: Ir al proceso de pago\n `$`("[data-testid='checkout-btn']").click()\n\n // Paso 4: Llenar el formulario\n `$`("[