Pruebas en Kotlin: unit, integration, e2e nivel avanzado

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: Por qué las pruebas avanzadas son la base del código Kotlin confiable

En el desarrollo moderno con Kotlin, escribir código es solo la mitad del trabajo. La verdadera maestría se demuestra en la capacidad de crear sistemas confiables, comprobables y fáciles de mantener. Las pruebas unitarias básicas son conocidas por todos, pero cuando el proyecto crece hasta una arquitectura de microservicios o una lógica de negocio compleja, las comprobaciones simples ya no son suficientes. Las pruebas avanzadas incluyen no solo el aislamiento de clases, sino también la verificación de la interacción entre componentes (pruebas de integración) y la reproducción completa de escenarios de usuario (E2E).

Este artículo está dirigido a desarrolladores que ya están familiarizados con los fundamentos de Kotlin y desean llevar la calidad de sus proyectos al siguiente nivel. Analizaremos tres niveles clave de pruebas: unit, integration y e2e, utilizando herramientas modernas: MockK, Testcontainers y Ktor Client. Aprenderá a escribir pruebas que no solo "pasen", sino que realmente protejan su código contra regresiones.



1. Pruebas unitarias: Aislamiento y mocks con MockK

Las pruebas unitarias son la base de la pirámide de pruebas. A nivel avanzado, no solo verificamos funciones, sino que probamos interacciones complejas entre objetos utilizando mocks. En Kotlin, la mejor biblioteca para esto es MockK.



1.1. Funcionalidades avanzadas de MockK

A diferencia de Mockito, MockK está escrito específicamente para Kotlin y admite corrutinas, lambdas y funciones de extensión. Veamos un ejemplo de prueba de un servicio que utiliza un repositorio y corrutinas:

// Servicio a probarclass UserService(private val repo: UserRepository) {    suspend fun getUserName(id: Long): String {        val user = repo.findById(id)        return user?.name ?: "Unknown"    }}

// Pruebaclass UserServiceTest { private val repo = mockk<UserRepository>() private val service = UserService(repo)

@Test fun `debería devolver el nombre del usuario cuando el usuario existe`() = runTest { // given coEvery { repo.findById(1L) } returns User(1, "Alice")

// when val result = service.getUserName(1L)

// then assertEquals("Alice", result) coVerify(exactly = 1) { repo.findById(1L) } }

@Test fun `debería devolver Unknown cuando el usuario no se encuentra`() = runTest { // given coEvery { repo.findById(any()) } returns null

// when val result = service.getUserName(99L)

// then assertEquals("Unknown", result) }}

Puntos clave:

  • coEvery y coVerify — para trabajar con funciones suspend.
  • mockk() crea un mock vacío, spyk() — un mock parcial de un objeto real.
  • Use slots para capturar argumentos.


1.2. Pruebas de lambdas y funciones de extensión

MockK permite simular funciones de extensión, lo cual es crítico para frameworks como Ktor o Spring WebFlux:

class StringValidator {    fun String.isValidEmail(): Boolean = this.contains("@")}

// Prueba@Testfun `prueba de mock de extensión`() { val validator = mockk<StringValidator>() with(validator) { every { "test@mail.com".isValidEmail() } returns true every { "invalid".isValidEmail() } returns false } assertTrue("test@mail.com".isValidEmail()) assertFalse("invalid".isValidEmail())}


2. Pruebas de integración: Base de datos y Testcontainers

Las pruebas de integración verifican cómo su código interactúa con dependencias reales: bases de datos, colas de mensajes, API externas. Aquí es donde Testcontainers viene al rescate: una biblioteca que levanta contenedores Docker directamente durante las pruebas.



2.1. Configuración de Testcontainers con PostgreSQL

Para una aplicación Spring Boot, se ve así:

Blogs

Book Recommendations