Skip to main content

8.2 Integration Testing with Testcontainers

← Back to Chapter 8: Testing, Deployment & Production-Ready Spring Boot

Using in-memory H2 databases for testing creates a dangerous false sense of security: H2 lacks real PostgreSQL/MySQL JSONB operators, dialect nuances, sequence behavior, and locking mechanics. Testcontainers spins up lightweight, real Docker instances of PostgreSQL, Redis, and Kafka during JUnit tests.


1. Why Testcontainers Beats H2 In-Memory DBs​

FeatureH2 In-Memory DatabaseTestcontainers (Real Docker DB)
FidelityEmulated, synthetic100% exact match with production engine
Dialect & FeaturesFails on Postgres JSONB, window functionsFull native SQL feature parity
Flyway MigrationsFrequently fails on complex vendor scriptsTests actual Flyway migrations cleanly
External DependenciesCannot test Kafka, Redis, ElasticsearchSpawns any official Docker image seamlessly

2. DynamicPropertySource with PostgreSQL Container​

Starting in Spring Boot 3.1+, integration with Testcontainers is completely streamlined via @ServiceConnection:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers
class CustomerRepositoryTest {

// Automatically spins up a real PostgreSQL container on an ephemeral Docker port!
@Container
@ServiceConnection // Spring Boot 3.1+ automatically wires datasource URL, username, password!
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine");

@Autowired
private CustomerRepository customerRepository;

@Test
void shouldPersistAndRetrieveCustomer() {
Customer customer = new Customer("Sarwvidya", "sarvy@codewithsarvy.dev");
Customer saved = customerRepository.save(customer);

assertNotNull(saved.getId());
assertEquals("Sarwvidya", customerRepository.findById(saved.getId()).orElseThrow().getFullName());
}
}

3. Testing Event-Driven Systems with KafkaContainer​

@Testcontainers
@SpringBootTest
class OrderKafkaEventTest {

@Container
@ServiceConnection
static ConfluentKafkaContainer kafka = new ConfluentKafkaContainer("confluentinc/cp-kafka:7.5.0");

@Autowired
private OrderEventProducer producer;

@Test
void shouldPublishEventToKafka() {
assertDoesNotThrow(() -> producer.publishOrderCreated(new OrderCreatedEvent(UUID.randomUUID())));
}
}

Testcontainers terminates and cleans up the Docker containers automatically when the test JVM process exits.