Create service connections from Testcontainers-managed containers

Building upon the auto-configuration support for service connections,
this commit adds support for deriving connection details from a
Testcontainers-managed container. Several service-specific
annotations have been introduced. These annotations can be used on a
container field to indicate that it is a source of the details for
a service connection.

See gh-34658

Co-Authored-By: Phillip Webb <pwebb@vmware.com>
Co-Authored-By: Mortitz Halbritter <mkammerer@vmware.com>
This commit is contained in:
Andy Wilkinson
2023-03-22 12:39:20 +00:00
parent 8ec266bea4
commit 95f45eab1f
80 changed files with 2748 additions and 264 deletions

View File

@@ -21,12 +21,11 @@ import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.redis.RedisServiceConnection;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testsupport.testcontainers.RedisContainer;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import static org.assertj.core.api.Assertions.assertThat;
@@ -35,6 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class SampleCacheApplicationRedisTests {
@Container
@RedisServiceConnection
private static final RedisContainer redis = new RedisContainer();
@Autowired
@@ -43,11 +43,6 @@ class SampleCacheApplicationRedisTests {
@Autowired
private CountryRepository countryRepository;
@DynamicPropertySource
static void redisProperties(DynamicPropertyRegistry properties) {
properties.add("spring.data.redis.url", () -> "redis://" + redis.getHost() + ":" + redis.getFirstMappedPort());
}
@Test
void validateCache() {
Cache countries = this.cacheManager.getCache("countries");

View File

@@ -18,4 +18,5 @@ dependencies {
testImplementation("io.projectreactor:reactor-test")
testImplementation("org.testcontainers:junit-jupiter")
testImplementation("org.testcontainers:postgresql")
testImplementation("org.testcontainers:r2dbc")
}

View File

@@ -24,35 +24,29 @@ import reactor.test.StepVerifier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest;
import org.springframework.boot.test.autoconfigure.jdbc.JdbcServiceConnection;
import org.springframework.boot.test.autoconfigure.r2dbc.R2dbcServiceConnection;
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link CityRepository}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
@Testcontainers(disabledWithoutDocker = true)
@DataR2dbcTest
class CityRepositoryTests {
@Container
@JdbcServiceConnection
@R2dbcServiceConnection
static PostgreSQLContainer<?> postgresql = new PostgreSQLContainer<>(DockerImageNames.postgresql())
.withDatabaseName("test_flyway");
@DynamicPropertySource
static void postgresqlProperties(DynamicPropertyRegistry registry) {
registry.add("spring.r2dbc.url", CityRepositoryTests::r2dbcUrl);
registry.add("spring.r2dbc.username", postgresql::getUsername);
registry.add("spring.r2dbc.password", postgresql::getPassword);
// configure flyway to use the same database
registry.add("spring.flyway.url", postgresql::getJdbcUrl);
registry.add("spring.flyway.user", postgresql::getUsername);
registry.add("spring.flyway.password", postgresql::getPassword);
}
@Autowired
private CityRepository repository;
@@ -63,9 +57,4 @@ class CityRepositoryTests {
.verifyComplete();
}
private static String r2dbcUrl() {
return String.format("r2dbc:postgresql://%s:%s/%s", postgresql.getHost(),
postgresql.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT), postgresql.getDatabaseName());
}
}

View File

@@ -21,4 +21,5 @@ dependencies {
testImplementation("io.projectreactor:reactor-test")
testImplementation("org.testcontainers:junit-jupiter")
testImplementation("org.testcontainers:postgresql")
testImplementation("org.testcontainers:r2dbc")
}

View File

@@ -24,35 +24,29 @@ import reactor.test.StepVerifier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest;
import org.springframework.boot.test.autoconfigure.jdbc.JdbcServiceConnection;
import org.springframework.boot.test.autoconfigure.r2dbc.R2dbcServiceConnection;
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link CityRepository}.
*
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
@Testcontainers(disabledWithoutDocker = true)
@DataR2dbcTest
class CityRepositoryTests {
@Container
@JdbcServiceConnection
@R2dbcServiceConnection
static PostgreSQLContainer<?> postgresql = new PostgreSQLContainer<>(DockerImageNames.postgresql())
.withDatabaseName("test_liquibase");
@DynamicPropertySource
static void postgresqlProperties(DynamicPropertyRegistry registry) {
registry.add("spring.r2dbc.url", CityRepositoryTests::r2dbcUrl);
registry.add("spring.r2dbc.username", postgresql::getUsername);
registry.add("spring.r2dbc.password", postgresql::getPassword);
// configure liquibase to use the same database
registry.add("spring.liquibase.url", postgresql::getJdbcUrl);
registry.add("spring.liquibase.user", postgresql::getUsername);
registry.add("spring.liquibase.password", postgresql::getPassword);
}
@Autowired
private CityRepository repository;
@@ -63,9 +57,4 @@ class CityRepositoryTests {
.verifyComplete();
}
private static String r2dbcUrl() {
return String.format("r2dbc:postgresql://%s:%s/%s", postgresql.getHost(),
postgresql.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT), postgresql.getDatabaseName());
}
}

View File

@@ -28,6 +28,7 @@ import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.mongo.MongoServiceConnection;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
@@ -40,8 +41,6 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@@ -51,6 +50,9 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link SampleSessionMongoApplication}.
*
* @author Angel L. Villalain
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers(disabledWithoutDocker = true)
@@ -63,14 +65,10 @@ class SampleSessionMongoApplicationTests {
private int port;
@Container
@MongoServiceConnection
static MongoDBContainer mongo = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(3)
.withStartupTimeout(Duration.ofMinutes(2));
@DynamicPropertySource
static void applicationProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", mongo::getReplicaSetUrl);
}
@Test
@SuppressWarnings("unchecked")
void sessionsEndpointShouldReturnUserSessions() {

View File

@@ -26,6 +26,7 @@ import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.redis.RedisServiceConnection;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.testsupport.testcontainers.RedisContainer;
@@ -37,8 +38,6 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@@ -48,23 +47,21 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link SampleSessionRedisApplication}.
*
* @author Angel L. Villalain
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers(disabledWithoutDocker = true)
class SampleSessionRedisApplicationTests {
@Container
@RedisServiceConnection
static RedisContainer redis = new RedisContainer();
@Autowired
private TestRestTemplate restTemplate;
@DynamicPropertySource
static void applicationProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.redis.host", redis::getHost);
registry.add("spring.data.redis.port", redis::getFirstMappedPort);
}
@Test
@SuppressWarnings("unchecked")
void sessionsEndpointShouldReturnUserSessions() {

View File

@@ -26,12 +26,11 @@ import org.testcontainers.junit.jupiter.Testcontainers;
import reactor.util.function.Tuples;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.mongo.MongoServiceConnection;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.boot.testsupport.testcontainers.DockerImageNames;
import org.springframework.http.HttpStatus;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
@@ -41,12 +40,16 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Vedran Pavic
* @author Scott Frederick
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
@SpringBootTest(properties = "spring.session.timeout:10", webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers(disabledWithoutDocker = true)
class SampleSessionWebFluxMongoApplicationTests {
@Container
@MongoServiceConnection
private static final MongoDBContainer mongo = new MongoDBContainer(DockerImageNames.mongo()).withStartupAttempts(3)
.withStartupTimeout(Duration.ofMinutes(2));
@@ -56,11 +59,6 @@ class SampleSessionWebFluxMongoApplicationTests {
@Autowired
private WebClient.Builder webClientBuilder;
@DynamicPropertySource
static void applicationProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", mongo::getReplicaSetUrl);
}
@Test
void userDefinedMappingsSecureByDefault() {
WebClient client = this.webClientBuilder.baseUrl("http://localhost:" + this.port + "/").build();

View File

@@ -25,12 +25,11 @@ import org.testcontainers.junit.jupiter.Testcontainers;
import reactor.util.function.Tuples;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.redis.RedisServiceConnection;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.boot.testsupport.testcontainers.RedisContainer;
import org.springframework.http.HttpStatus;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
@@ -40,12 +39,16 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Vedran Pavic
* @author Scott Frederick
* @author Moritz Halbritter
* @author Andy Wilkinson
* @author Phillip Webb
*/
@SpringBootTest(properties = "spring.session.timeout:10", webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers(disabledWithoutDocker = true)
class SampleSessionWebFluxRedisApplicationTests {
@Container
@RedisServiceConnection
private static final RedisContainer redis = new RedisContainer();
@LocalServerPort
@@ -54,12 +57,6 @@ class SampleSessionWebFluxRedisApplicationTests {
@Autowired
private WebClient.Builder webClientBuilder;
@DynamicPropertySource
static void applicationProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.redis.host", redis::getHost);
registry.add("spring.data.redis.port", redis::getFirstMappedPort);
}
@Test
void userDefinedMappingsSecureByDefault() {
WebClient client = this.webClientBuilder.baseUrl("http://localhost:" + this.port + "/").build();