@SpringBootTest@TestcontainersclassApplicationTests{@ContainerstaticPostgreSQLContainer<?>postgres=newPostgreSQLContainer<>("postgres:15-alpine").withDatabaseName("testdb");@DynamicPropertySourcestaticvoidconfigureProperties(DynamicPropertyRegistryregistry){registry.add("spring.datasource.url",postgres::getJdbcUrl);registry.add("spring.datasource.username",postgres::getUsername);registry.add("spring.datasource.password",postgres::getPassword);registry.add("spring.jpa.hibernate.ddl-auto",()->"create-drop");}@TestvoidcontextLoads(){// Test with real database}}
@TestcontainersclassKafkaIntegrationTest{@ContainerstaticKafkaContainerkafka=newKafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:7.5.0"));@DynamicPropertySourcestaticvoidkafkaProperties(DynamicPropertyRegistryregistry){registry.add("spring.kafka.bootstrap-servers",kafka::getBootstrapServers);}@AutowiredprivateKafkaTemplate<String,String>kafkaTemplate;@AutowiredprivateMessageListenermessageListener;@TestvoidshouldProcessMessage()throwsException{Stringtopic="test-topic";Stringmessage="Hello Kafka!";kafkaTemplate.send(topic,message);// Wait for message processingawait().atMost(Duration.ofSeconds(10)).until(()->messageListener.getReceivedMessages().size()==1);assertEquals(message,messageListener.getReceivedMessages().get(0));}}
Use Case 3: Multi-Container Microservicios Prueba¶
@TestcontainersclassMicroservicesIntegrationTest{staticNetworknetwork=Network.newNetwork();@ContainerstaticPostgreSQLContainer<?>database=newPostgreSQLContainer<>("postgres:15-alpine").withNetwork(network).withNetworkAliases("database").withDatabaseName("orders");@ContainerstaticGenericContainer<?>redis=newGenericContainer<>("redis:7-alpine").withNetwork(network).withNetworkAliases("redis").withExposedPorts(6379);@ContainerstaticGenericContainer<?>orderService=newGenericContainer<>("order-service:latest").withNetwork(network).withExposedPorts(8080).withEnv("DATABASE_URL","jdbc:postgresql://database:5432/orders").withEnv("REDIS_HOST","redis").dependsOn(database,redis).waitingFor(Wait.forHttp("/actuator/health").forStatusCode(200));@TestvoidshouldCreateOrder(){StringbaseUrl="http://"+orderService.getHost()+":"+orderService.getMappedPort(8080);// Make HTTP request to order serviceRestTemplaterestTemplate=newRestTemplate();Orderorder=newOrder("item-123",2);ResponseEntity<Order>response=restTemplate.postForEntity(baseUrl+"/orders",order,Order.class);assertEquals(HttpStatus.CREATED,response.getStatusCode());assertNotNull(response.getBody().getId());}}
@Testcontainers@DataMongoTestclassProductRepositoryTest{@ContainerstaticMongoDBContainermongodb=newMongoDBContainer("mongo:6.0").withExposedPorts(27017);@DynamicPropertySourcestaticvoidmongoProperties(DynamicPropertyRegistryregistry){registry.add("spring.data.mongodb.uri",mongodb::getReplicaSetUrl);}@AutowiredprivateProductRepositoryproductRepository;@TestvoidshouldFindProductsByCategory(){// Insert test dataproductRepository.save(newProduct("Laptop","Electronics",999.99));productRepository.save(newProduct("Mouse","Electronics",29.99));productRepository.save(newProduct("Desk","Furniture",299.99));// Query by categoryList<Product>electronics=productRepository.findByCategory("Electronics");assertEquals(2,electronics.size());assertTrue(electronics.stream().allMatch(p->p.getCategory().equals("Electronics")));}}
Use contenedores estáticos para clases de prueba: Compartir contenedores en todos los métodos de prueba para reducir el tiempo de inicio y el uso de recursos
Reutilización del contenedor de implementación: Habilitar testcontainers.reuse.enable=true para ciclos de prueba de desarrollo local más rápidos
Clean up resources: Utilizar @AfterAll o tratar con recursos cuando se administran manualmente contenedores fuera de la integración de JUnit
Utilice etiquetas de imagen específicas: Evite latest_ tag; utilice versiones específicas como postgres:15-alpine para pruebas reproducibles
Empiezo de contenedores paralelos: Utilice Startables.deepStart() para iniciar múltiples contenedores independientes simultáneamente
Minimize container restarts: Utilice el nivel de clase @Container en lugar de nivel de método cuando sea posible
Elija imágenes livianas: Preferir imágenes basadas en Alpine (por ejemplo, postgres:15-alpine_) para reducir el tiempo y el uso del disco
La implementación espera estrategias sabiamente: Utilizar estrategias de espera apropiadas para evitar retrasos innecesarios y asegurar la preparación de contenedores
Prueba contra las dependencias de producción* Utilice motores de base de datos reales y corredores de mensajes en lugar de versiones incrustadas
** Datos de prueba de aislamiento: Asegurar que cada prueba crea y limpia sus propios datos para prevenir las interdependencias de las pruebas
Usar alias de red: Crear redes personalizadas y utilizar alias significativos para la comunicación entre contenedores
Implement health checks: Siempre configurar estrategias de espera adecuadas para asegurar que los contenedores estén completamente listos antes de ejecutar las pruebas
Verify Docker availability: Asegurar que el entorno CI tiene Docker instalado y accesible
Configure timeouts appropriately: Establecer fechas de inicio realistas considerando el rendimiento del entorno CI
Use el caché de la imagen: Configure las imágenes de Docker para acelerar las carreras posteriores
** Utilización de los recursos de monitor**: Tenga en cuenta los límites de memoria y CPU en entornos CI; ajuste las configuraciones de contenedores en consecuencia
Keep TestContainers actualizados: Actualización regular a las últimas versiones para parches de seguridad y nuevas características
Utilizar imágenes oficiales: Preferir imágenes oficiales de Docker de editores verificados
Evitar el modo privilegiado Sólo uso .withPrivilegedMode()_ cuando sea absolutamente necesario
Revisar los puertos expuestos: Sólo exponga los puertos que son realmente necesarios para la prueba