TestContainers # Cheatsheet¶
Installazione¶
Prerequisiti_TABLE_146___¶
Maven Dependencies¶
<!-- Core TestContainers -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.19.3</version>
<scope>test</scope>
</dependency>
<!-- JUnit 5 Integration -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.3</version>
<scope>test</scope>
</dependency>
<!-- Database Modules -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.19.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId>
<version>1.19.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mongodb</artifactId>
<version>1.19.3</version>
<scope>test</scope>
</dependency>
Dipendenze di livello¶
// Kotlin DSL
testImplementation("org.testcontainers:testcontainers:1.19.3")
testImplementation("org.testcontainers:junit-jupiter:1.19.3")
testImplementation("org.testcontainers:postgresql:1.19.3")
testImplementation("org.testcontainers:mysql:1.19.3")
testImplementation("org.testcontainers:kafka:1.19.3")
// Groovy DSL
testImplementation 'org.testcontainers:testcontainers:1.19.3'
testImplementation 'org.testcontainers:junit-jupiter:1.19.3'
testImplementation 'org.testcontainers:postgresql:1.19.3'
Docker Setup by Platform¶
Tabella_147_
Comandi di base¶
Container Lifecycle Operations¶
Tabella_148_
Generic Container Creation¶
Tabella_149_
Database Containers¶
TABELLA
Uso avanzato¶
Wait Strategies¶
Tabella_151_
File and Volume Operations¶
Tabella_152_
Networking¶
Tabella_153_
Esecuzione del contenitore¶
Tabella_154_
JUnit 5 Integrazione¶
Tabella_155_
Configurazione¶
TestContainers Properties¶
Crea testcontainers.properties_ in src/test/resources:
# Docker host configuration
docker.host=unix:///var/run/docker.sock
# docker.host=tcp://localhost:2375
# Container reuse (requires Docker labels support)
testcontainers.reuse.enable=true
# Image pull policy
testcontainers.image.pull.policy=DefaultPullPolicy
# Ryuk container (cleanup)
testcontainers.ryuk.disabled=false
testcontainers.ryuk.container.image=testcontainers/ryuk:0.5.1
# Checks
testcontainers.checks.disable=false
Docker Compose Integration¶
@Container
static DockerComposeContainer environment =
new DockerComposeContainer(new File("docker-compose.yml"))
.withExposedService("db", 5432)
.withExposedService("redis", 6379)
.withLocalCompose(true)
.withPull(true)
.waitingFor("db", Wait.forHealthcheck());
Configurazione del contenitore personalizzata¶
public class CustomPostgreSQLContainer extends PostgreSQLContainer<CustomPostgreSQLContainer> {
private static final String IMAGE_VERSION = "postgres:15-alpine";
public CustomPostgreSQLContainer() {
super(IMAGE_VERSION);
this.withDatabaseName("testdb")
.withUsername("testuser")
.withPassword("testpass")
.withInitScript("init.sql")
.withCommand("postgres -c max_connections=200");
}
}
Spring Boot Integration¶
@SpringBootTest
@Testcontainers
class ApplicationTests {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15-alpine")
.withDatabaseName("testdb");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
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");
}
@Test
void contextLoads() {
// Test with real database
}
}
Common Use Cases¶
Use Case 1: PostgreSQL Integration Test¶
@Testcontainers
@SpringBootTest
class UserRepositoryTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15-alpine")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test")
.withInitScript("schema.sql");
@DynamicPropertySource
static void registerProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Autowired
private UserRepository userRepository;
@Test
void shouldSaveAndFindUser() {
User user = new User("john@example.com", "John Doe");
userRepository.save(user);
Optional<User> found = userRepository.findByEmail("john@example.com");
assertTrue(found.isPresent());
assertEquals("John Doe", found.get().getName());
}
}
Use Case 2: Kafka Message Processing Test¶
@Testcontainers
class KafkaIntegrationTest {
@Container
static KafkaContainer kafka = new KafkaContainer(
DockerImageName.parse("confluentinc/cp-kafka:7.5.0")
);
@DynamicPropertySource
static void kafkaProperties(DynamicPropertyRegistry registry) {
registry.add("spring.kafka.bootstrap-servers", kafka::getBootstrapServers);
}
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
@Autowired
private MessageListener messageListener;
@Test
void shouldProcessMessage() throws Exception {
String topic = "test-topic";
String message = "Hello Kafka!";
kafkaTemplate.send(topic, message);
// Wait for message processing
await().atMost(Duration.ofSeconds(10))
.until(() -> messageListener.getReceivedMessages().size() == 1);
assertEquals(message, messageListener.getReceivedMessages().get(0));
}
}
Use Case 3: Multi-Container Microservizi Test¶
@Testcontainers
class MicroservicesIntegrationTest {
static Network network = Network.newNetwork();
@Container
static PostgreSQLContainer<?> database = new PostgreSQLContainer<>("postgres:15-alpine")
.withNetwork(network)
.withNetworkAliases("database")
.withDatabaseName("orders");
@Container
static GenericContainer<?> redis = new GenericContainer<>("redis:7-alpine")
.withNetwork(network)
.withNetworkAliases("redis")
.withExposedPorts(6379);
@Container
static GenericContainer<?> orderService = new GenericContainer<>("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));
@Test
void shouldCreateOrder() {
String baseUrl = "http://" + orderService.getHost() + ":"
+ orderService.getMappedPort(8080);
// Make HTTP request to order service
RestTemplate restTemplate = new RestTemplate();
Order order = new Order("item-123", 2);
ResponseEntity<Order> response = restTemplate.postForEntity(
baseUrl + "/orders", order, Order.class
);
assertEquals(HttpStatus.CREATED, response.getStatusCode());
assertNotNull(response.getBody().getId());
}
}
Use Case 4: MongoDB Integration Test¶
@Testcontainers
@DataMongoTest
class ProductRepositoryTest {
@Container
static MongoDBContainer mongodb = new MongoDBContainer("mongo:6.0")
.withExposedPorts(27017);
@DynamicPropertySource
static void mongoProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", mongodb::getReplicaSetUrl);
}
@Autowired
private ProductRepository productRepository;
@Test
void shouldFindProductsByCategory() {
// Insert test data
productRepository.save(new Product("Laptop", "Electronics", 999.99));
productRepository.save(new Product("Mouse", "Electronics", 29.99));
productRepository.save(new Product("Desk", "Furniture", 299.99));
// Query by category
List<Product> electronics = productRepository.findByCategory("Electronics");
assertEquals(2, electronics.size());
assertTrue(electronics.stream()
.allMatch(p -> p.getCategory().equals("Electronics")));
}
}
Use Case 5: Docker Compose Test Environment¶
@Testcontainers
class FullStackIntegrationTest {
@Container
static DockerComposeContainer<?> environment = new DockerComposeContainer<>(
new File("src/test/resources/docker-compose-test.yml")
)
.withExposedService("api", 8080,
Wait.forHttp("/health").forStatusCode(200))
.withExposedService("postgres", 5432,
Wait.forListeningPort())
.withExposedService("redis", 6379)
.withLocalCompose(true);
@Test
void shouldConnectToAllServices() {
String apiHost = environment.getServiceHost("api", 8080);
Integer apiPort = environment.getServicePort("api", 8080);
String apiUrl = "http://" + apiHost + ":" + apiPort;
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity(
apiUrl + "/health", String.class
);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
}
Migliori Pratiche¶
Gestione del contenitore¶
- ** Utilizzare contenitori statici per classi di test**: Condividere contenitori attraverso tutti i metodi di test per ridurre il tempo di avvio e l'utilizzo delle risorse
- **Riutilizzo container **: Abilita
testcontainers.reuse.enable=trueper cicli di test di sviluppo locali più veloci - **Creare risorse ** Utilizzare
@AfterAllo provare-con-risorse quando si gestisce manualmente i contenitori al di fuori dell'integrazione JUnit - Utilizzare tag di immagine specifici: Evitare
latest_ tag; utilizzare versioni specifiche comepostgres:15-alpineper prove riproducibili
Ottimizzazione delle prestazioni¶
- l'avvio dei container parallel. Usa
Startables.deepStart()per avviare simultaneamente più contenitori indipendenti - Minimize container restarts**: Utilizzare il livello di classe
@Containerinvece di livello di metodo quando possibile - Scegli immagini leggere: Preferisci immagini a base alpina (ad esempio
postgres:15-alpine) per ridurre il tempo di estrazione e l'utilizzo del disco - **Aspetta le strategie con saggezza ** Utilizzare strategie di attesa appropriate per evitare ritardi inutili, assicurando la disponibilità dei container
Testing Strategy¶
- **Test contro le dipendenze della produzione ** Utilizzare veri motori di database e broker di messaggi invece di versioni incorporate
- ** Dati di prova Isolate**: Assicurarsi che ogni test crei e determini i propri dati per prevenire interdipendenze di prova
- Utilizzare gli alias di rete: Creare reti personalizzate e utilizzare alias significativi per la comunicazione intercontainer
- Implementare i controlli sanitari Configurare sempre strategie di attesa adeguate per garantire che i contenitori siano completamente pronti prima dell'esecuzione dei test
CI/CD Integrazione¶
- Verify Docker disponibilità: Assicurare l'ambiente CI ha Docker installato e accessibile
- Configurare i timeout in modo appropriato: Impostare tempi di avvio realistici considerando le prestazioni dell'ambiente CI
- Utilizzare la cache dell'immagine: Configurare CI nella cache Immagini Docker per velocizzare le operazioni successive
- ** Uso delle risorse motorie** Essere consapevoli dei limiti di memoria e CPU negli ambienti CI; regolare le configurazioni dei container di conseguenza
Sicurezza e manutenzione¶
- Keep TestContainers aggiornato: Aggiornamento regolare alle ultime versioni per patch di sicurezza e nuove funzionalità
- Utilizza immagini ufficiali: Preferisci le immagini ufficiali Docker da editori verificati
- Avoid modalità privilegiata: Utilizzare solo
.withPrivilegedMode()quando assolutamente necessario - **Recensione porte esposte ** Solo esporre le porte che sono effettivamente necessari per il test
Risoluzione dei problemi¶
Tabella_156
Moduli disponibili¶
TABLE_157| Nginx