CodeGym /Cursos /Módulo 5. Spring /Lección 269: Práctica: configuración de Testcontainers pa...

Lección 269: Práctica: configuración de Testcontainers para probar con bases de datos

Módulo 5. Spring
Nivel 21 , Lección 8
Disponible

En las lecciones anteriores nos metimos en el testing de aplicaciones microservicio. Vimos los diferentes enfoques y tipos de testing, incluyendo unit-testing con JUnit y Mockito, integration testing de REST API con MockMvc, y también revisamos contract testing con Pact. Empezamos a elegir herramientas que ayudan a asegurar la fiabilidad en la interacción entre componentes y servicios. Ahora ha llegado el momento de ver cómo testar la interacción con bases de datos usando una herramienta basada en Docker — Testcontainers.

Antes comprobamos que probar con bases de datos puede ser complicado por la necesidad de aislar datos y crear un entorno realista. ¡Hoy vamos a solucionarlo!

¿Para qué sirven Testcontainers?

Imagínate que estás testeando la interacción con una base de datos y de repente alguien añade datos no válidos a la base de test. Tus tests fallan, te pones a investigar y es un caos. O otra situación: pruebas la app con PostgreSQL, pero en producción tendrás MySQL. ¿Qué hacer? El entorno para tests debe ser:

  1. Aislado — para que cada ejecución del test no dependa de factores externos.
  2. Realista — para que los tests se ejecuten con las mismas configuraciones que en producción.
  3. Fácil de configurar — para no requerir una infraestructura compleja.

Testcontainers permite arrancar contenedores Docker durante la ejecución de los tests, proporcionando un entorno aislado y realista para testear. Esto significa que puedes usar las mismas bases de datos, brokers de mensajes y otras dependencias que se usan en el mundo real. ¿Listo? ¡Vamos!


Preparación para trabajar con Testcontainers

  1. Añadamos dependencias

    Para empezar hay que añadir Testcontainers a tu proyecto. Si usas PostgreSQL (elección favorita de muchos testers), tus dependencias en pom.xml se verán así:

    
    <dependencies>
        <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>
    </dependencies>
    

    Si usas Gradle:

    
    testImplementation 'org.testcontainers:junit-jupiter:1.19.0'
    testImplementation 'org.testcontainers:postgresql:1.19.0'
    

    Testcontainers soporta muchas otras bases de datos, como MySQL, MongoDB, Oracle, e incluso Kafka y RabbitMQ. La lista completa está en la documentación oficial de Testcontainers.

  2. Asegúrate de que Docker esté instalado Testcontainers funciona a través de Docker, así que verifica que Docker esté instalado y en ejecución en tu máquina.
  3. Configuración mínima de Docker Debes asegurarte de que Docker pueda arrancar contenedores con bases de datos. Una comprobación sencilla es ejecutar el comando docker run directamente para PostgreSQL:
    docker run --name postgres-test -e POSTGRES_PASSWORD=test -d postgres:latest
    

    Si el contenedor arranca sin errores, ¡estás listo para seguir!


Configuración de la prueba usando Testcontainers

1. Arrancar un contenedor PostgreSQL

Empecemos creando el contenedor PostgreSQL más simple para testing. Aquí tienes un ejemplo:


import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class PostgresContainerTest {

    @Test
    void testPostgresContainer() {
        // Creamos el contenedor PostgreSQL
        try (PostgreSQLContainer
    postgresContainer = new PostgreSQLContainer<>("postgres:latest")) {
            postgresContainer.start();

            // Comprobamos que la base esté levantada
            String jdbcUrl = postgresContainer.getJdbcUrl();
            String username = postgresContainer.getUsername();
            String password = postgresContainer.getPassword();

            System.out.println("JDBC URL: " + jdbcUrl);
            System.out.println("User: " + username);
            System.out.println("Password: " + password);

            assertEquals("test", username);
        }
    }
}

Este test arranca un contenedor PostgreSQL, obtiene la URL para conectarse a la base y muestra cómo puedes usarla. Claro, la utilidad práctica por ahora es limitada, pero es solo el comienzo.


2. Integración con Spring Data JPA

Ahora conectaremos Testcontainers con tu aplicación Spring Boot. Supongamos que tu app usa JPA, y en los tests levantaremos un contenedor PostgreSQL.

Configuración de application.properties para tests

Crea el archivo application-test.properties en src/test/resources:

spring.datasource.url=jdbc:tc:postgresql:latest:///testdb
spring.datasource.username=test
spring.datasource.password=test
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
Atención:
jdbc:tc:postgresql:latest:///testdb — esto es una pequeña magia de Testcontainers. Levantará automáticamente un contenedor PostgreSQL y enlazará sus datos.

Ejemplo de test usando JPA

Supongamos que tienes la entidad User y el repositorio UserRepository:


@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    // Constructores, getters y setters
}


@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

Creemos un test de integración para el repositorio:


@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.properties")
public class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    void testSaveUser() {
        // Creamos un usuario nuevo
        User user = new User();
        user.setName("Test User");

        // Lo guardamos en la base
        User savedUser = userRepository.save(user);

        // Comprobamos el resultado
        assertNotNull(savedUser.getId());
        assertEquals("Test User", savedUser.getName());
    }
}

Ahora, al ejecutar el test, Spring Boot levantará automáticamente el contenedor PostgreSQL mediante Testcontainers.


3. Control manual del contenedor

En escenarios complejos puedes querer gestionar el contenedor manualmente. Por ejemplo:


@Testcontainers
@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.properties")
public class ManualPostgresContainerTest {

    @Container
    private static PostgreSQLContainer
    postgresContainer = new PostgreSQLContainer<>("postgres:latest");

    @DynamicPropertySource
    static void registerProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgresContainer::getJdbcUrl);
        registry.add("spring.datasource.username", postgresContainer::getUsername);
        registry.add("spring.datasource.password", postgresContainer::getPassword);
    }

    @Autowired
    private UserRepository userRepository;

    @Test
    void testSaveUserManualContainer() {
        User user = new User();
        user.setName("Another User");
        User savedUser = userRepository.save(user);

        assertNotNull(savedUser.getId());
        assertEquals("Another User", savedUser.getName());
    }
}

Aquí @Container gestiona el ciclo de vida del contenedor, y @DynamicPropertySource permite inyectar propiedades dinámicas en el contexto de Spring.


Errores comunes y cómo solucionarlos

  1. Docker no está arrancado Si Docker no funciona, Testcontainers no podrá levantar contenedores. Asegúrate de que Docker esté corriendo correctamente.
  2. Problemas de red A veces los contenedores no pueden comunicarse con la aplicación por la configuración de red. Revisa si trabajas en modo aislado de Docker (por ejemplo, sin Internet).
  3. Falta de recursos Las bases de datos pueden ser "pesadas" para un equipo local. Asegúrate de que tu máquina tenga suficiente memoria y CPU para ejecutar los contenedores.

Aplicación práctica

Acabas de ver cómo Testcontainers puede facilitar la vida al desarrollador y al tester. Testear con bases de datos reales ahora es accesible y sencillo, y los tests son más fiables. Esta herramienta es especialmente útil en entrevistas, donde es importante demostrar seguridad en el testing de microservicios. En proyectos reales ayuda a evitar errores en la interacción con sistemas de producción.

Para seguir aprendiendo, recomendación: echa un vistazo a la documentación de Testcontainers.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION