Cuando hablamos de microservicios, las bases de datos juegan un papel clave. Tu código puede parecer perfecto, pero si las consultas SQL no funcionan como deberían, o la estructura de la base de datos difiere de lo esperado, el resultado puede ser desastroso. Y ahora imagina que tienes 10 microservicios en el sistema. Los errores a este nivel pueden, literalmente, sumir el sistema en el caos.
Las pruebas de integración con la base de datos te ayudarán a:
- Comprobar que las consultas SQL funcionan correctamente.
- Asegurar que las anotaciones JPA y las entidades están configuradas correctamente.
- Ejecutar pruebas de migraciones de bases de datos (por ejemplo, Flyway o Liquibase).
- Asegurar que las transacciones se gestionan correctamente.
Sin embargo, levantar una base de datos real para cada prueba es difícil y lleva tiempo. Aquí es donde aparece Testcontainers.
¿Qué es Testcontainers?
Testcontainers — es una librería Java para escribir tests que usan contenedores Docker. Permite levantar y destruir contenedores temporales para bases de datos, brokers de mensajes, servidores web y otros servicios, directamente durante la ejecución de los tests. La belleza está en que, cuando se ejecutan las pruebas, los contenedores se levantan según sea necesario y, al terminar las pruebas, se destruyen automáticamente.
Ejemplo de la vida real: imagina que coges un coche en un servicio de carsharing. Lo usas sólo cuando lo necesitas y lo devuelves después. Testcontainers funciona de forma análoga con contenedores Docker para tests.
¿Cómo funciona Testcontainers?
- Indicas qué contenedor hay que arrancar (por ejemplo, PostgreSQL).
- El contenedor se levanta antes del test.
- El código del test se conecta al contenedor y prueba la interacción.
- Tras terminar el test, el contenedor se elimina automáticamente de tu máquina.
La principal ventaja: No hay dependencia de bases de datos reales ni de servicios externos. Todo está aislado.
Instalación de Testcontainers
Para empezar añadimos las dependencias en pom.xml (si usas Maven):
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.19.0</version>
<scope>test</scope>
</dependency>
Si usas Gradle, añadimos:
testImplementation 'org.testcontainers:junit-jupiter:1.19.0'
testImplementation 'org.testcontainers:postgresql:1.19.0'
Además, asegúrate de que tienes Docker instalado y funcionando. Si usas Docker Desktop, ya puedes alegrarte.
Tu primer test con Testcontainers
Empecemos creando el test más simple. Levantaremos un contenedor PostgreSQL y comprobaremos que podemos conectarnos:
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class PostgresTest {
@Test
public void testPostgresContainer() {
// Creamos contenedor PostgreSQL
try (PostgreSQLContainer
postgres = new PostgreSQLContainer<>("postgres:15.1")) {
// Iniciamos el contenedor
postgres.start();
// Comprobamos que el contenedor está iniciado y que la URL de conexión no está vacía
assertNotNull(postgres.getJdbcUrl());
System.out.println("PostgreSQL iniciado en " + postgres.getJdbcUrl());
}
}
}
Si ejecutas este test, Docker levantará el contenedor PostgreSQL, ejecutará la prueba y destruirá el contenedor. Mira la consola: debería aparecer información sobre la base de datos arrancada.
Integración con Spring Data JPA
Ahora conectemos Testcontainers a una aplicación real. Usaremos Spring Boot y JPA.
1. Configuración de Spring Boot
En el archivo application.properties indica parámetros "placeholder" para la base de datos (serán sobrescritos por Testcontainers durante las pruebas):
spring.datasource.url=jdbc:tc:postgresql:15.1://localhost/testdb
spring.datasource.username=test
spring.datasource.password=test
spring.jpa.hibernate.ddl-auto=update
Fíjate en el formato jdbc:tc:postgresql:15.1://localhost/testdb. ¡Testcontainers tirará automáticamente del contenedor PostgreSQL de la versión indicada!
2. Creación de la entidad
Añadamos la entidad User:
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
// getters y setters
}
3. Creación del repositorio
Creemos una interfaz sencilla de repositorio:
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
}
4. Probando el repositorio con Testcontainers
Añadamos tests para verificar las operaciones CRUD. Testcontainers creará el contenedor PostgreSQL al ejecutar los tests:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DataJpaTest
public class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
@Test
public void testSaveUser() {
// Guardamos un nuevo usuario
User user = new User();
user.setName("John Doe");
User savedUser = userRepository.save(user);
// Comprobamos que el usuario se guardó
assertEquals("John Doe", savedUser.getName());
}
}
Cuando ejecutes este test, Testcontainers levantará el contenedor PostgreSQL, creará la base de datos testdb, y luego ejecutará las operaciones SQL. Al finalizar, el contenedor se destruirá automáticamente.
Ventajas de Testcontainers
- Aislamiento del entorno: cada base de datos de prueba se ejecuta en un contenedor separado.
- Configuración mínima: no hace falta levantar bases de datos manualmente ni borrarlas después de las pruebas.
- Realismo: pruebas tu código contra una base de datos real, no contra una emulación.
- Soporte para muchas bases de datos y servicios: por ejemplo, MySQL, MongoDB, Redis, Kafka.
Errores típicos y particularidades de uso
- Error "Docker not found". Asegúrate de que Docker está en ejecución y que tu entorno de desarrollo tiene acceso a él.
- Pruebas lentas. Levantar un contenedor lleva tiempo. Usa contenedores compartidos para un conjunto de tests para acelerar el proceso.
- Fugas de recursos. No olvides cerrar los contenedores después de las pruebas si los levantas manualmente (si no usas la gestión automática de Testcontainers).
¿Dónde te será útil?
- Desarrollo de microservicios: levantas una base real o un broker de mensajes (Kafka, RabbitMQ) para las pruebas.
- Comprobación de migraciones: asegúrate de que tus migraciones de Flyway funcionan en todas las bases objetivo.
- Entrevistas: si te preguntan "¿Cómo pruebas las bases de datos en tus proyectos?", responder "Testcontainers" despertará interés y sumará puntos.
Ahora sabes cómo Testcontainers te permite levantar un entorno real para pruebas directamente desde el código. Ejecuta las pruebas y siente la magia del aislamiento de Docker. En las siguientes lecciones seguiremos explorando microservicios y otras herramientas de testing.
GO TO FULL VERSION