Data Operations with Testcontainers¶
Repository tests need a real database. Testcontainers starts the store in a Docker container for the duration of the test and tears it down afterwards. The samples use two wiring styles — one for plain Spring applications and one for Spring Boot — plus Boot's automatic @ServiceConnection variant.
Tip
Pair Testcontainers with StepVerifier (see Testing) to assert the reactive query results.
Plain Spring: ApplicationContextInitializer¶
The non-Boot samples (cache, the r2dbc-* modules) implement an ApplicationContextInitializer that starts the container, maps its properties into the environment, and stops it when the context closes:
@TestConfiguration(proxyBeanMethods = false)
public class ContainersConfiguration implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final PostgreSQLContainer POSTGRES = new PostgreSQLContainer(
DockerImageName.parse("postgres:18.4-alpine"))
.withCopyFileToContainer(
MountableFile.forClasspathResource("init.sql"),
"/docker-entrypoint-initdb.d/init.sql");
@Override
public void initialize(ConfigurableApplicationContext context) {
if (!POSTGRES.isRunning()) {
POSTGRES.start();
}
context.addApplicationListener((ContextClosedEvent event) -> POSTGRES.stop());
TestPropertyValues.of(
"r2dbc.host=" + POSTGRES.getHost(),
"r2dbc.port=" + POSTGRES.getFirstMappedPort(),
"r2dbc.username=" + POSTGRES.getUsername(),
"r2dbc.password=" + POSTGRES.getPassword(),
"r2dbc.database=" + POSTGRES.getDatabaseName()
).applyTo(context.getEnvironment());
}
}
The initializer is applied through @ContextConfiguration, so it is reusable across test classes:
@ContextConfiguration(initializers = { ContainersConfiguration.class })
@SpringJUnitConfig(classes = { PostRepositoryTest.TestConfig.class })
public class PostRepositoryTest {
@Autowired
PostRepository posts;
@Test
public void testSaveAll() {
var post = posts.save(Post.of("test", "content")).block();
assertThat(posts.findById(post.id()).block()).isNotNull();
}
}
Because the properties use the application's own keys (r2dbc.* in the samples), no container library knowledge leaks into the configuration classes — see the cache sample for the complete pattern, and the r2dbc-connection-factories tests for raw-driver variants.
Spring Boot with @DynamicPropertySource¶
For Boot applications, the @Testcontainers + @Container + @DynamicPropertySource combination starts the container with the JUnit lifecycle and maps its runtime values into arbitrary property keys. From the boot-data-mongo-template sample:
@DataMongoTest
@Import({PostRepository.class, TestcontainersConfiguration.class})
public class PostRepositoryTest {
@Container
static MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:latest"));
@DynamicPropertySource
static void mongoProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl);
}
@Autowired
ReactiveMongoTemplate reactiveMongoTemplate;
@Autowired
PostRepository postRepository;
}
Notes:
@Testcontainers(class level) enables the JUnit extension; a static@Containerfield starts before all tests and stops after them.- The
@DynamicPropertySourcemethod receives aDynamicPropertyRegistry— register the connection properties as suppliers, resolved lazily once the container is up. - Any property key works here:
spring.data.mongodb.uri,spring.r2dbc.url,spring.neo4j.uri, or custom keys.
Spring Boot with @ServiceConnection¶
Boot 3.1+ removes even the property mapping: a container bean annotated with @ServiceConnection contributes the connection properties automatically — Boot recognizes the container type and registers the right properties. From the boot-data-mongo sample:
@TestConfiguration(proxyBeanMethods = false)
class TestcontainersConfiguration {
@Bean
@ServiceConnection
MongoDBContainer mongoDbContainer() {
return new MongoDBContainer(DockerImageName.parse("mongo:latest"));
}
}
@DataMongoTest
@Import(TestcontainersConfiguration.class)
@ActiveProfiles("test")
public class PostRepositoryTest {
@Autowired
ReactiveMongoTemplate reactiveMongoTemplate;
@Autowired
PostRepository postRepository;
}
No @DynamicPropertySource, no property names — Boot wires spring.data.mongodb.uri (or spring.r2dbc.url for a PostgreSQLContainer, spring.neo4j.uri for a Neo4jContainer, and so on) from the container itself. The same pattern appears with CassandraContainer, CouchbaseContainer, ElasticsearchContainer, and Neo4jContainer across the Boot data samples — see the Data Operations chapter pages for each store's snippet.
Choosing a style¶
| Style | Use when |
|---|---|
ApplicationContextInitializer + TestPropertyValues |
Plain Spring (no Boot) tests; reusable initializer shared by many test classes |
@Testcontainers + @DynamicPropertySource |
Boot tests needing explicit control of property keys or container options |
@ServiceConnection |
Boot tests with a supported container type — the least code, zero property names |