此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Boot 3.4.3spring-doc.cadn.net.cn

测试容器

Testcontainers 库提供了一种管理在 Docker 容器中运行的服务的方法。 它与 JUnit 集成,允许您编写一个测试类,该类可以在任何测试运行之前启动容器。 Testcontainers 对于编写与实际后端服务(如 MySQL、MongoDB、Cassandra 等)通信的集成测试特别有用。spring-doc.cadn.net.cn

Testcontainers 可以在 Spring Boot 测试中使用,如下所示:spring-doc.cadn.net.cn

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import org.springframework.boot.test.context.SpringBootTest;

@Testcontainers
@SpringBootTest
class MyIntegrationTests {

	@Container
	static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");

	@Test
	void myTest() {
		// ...
	}

}
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection

@Testcontainers
@SpringBootTest
class MyIntegrationTests {

	@Test
	fun myTest() {
		// ...
	}

	companion object {
		@Container
		@JvmStatic
		val neo4j = Neo4jContainer("neo4j:5");
	}
}

This will start up a docker container running Neo4j (if Docker is running locally) before any of the tests are run. In most cases, you will need to configure the application to connect to the service running in the container.spring-doc.cadn.net.cn

Service Connections

A service connection is a connection to any remote service. Spring Boot’s auto-configuration can consume the details of a service connection and use them to establish a connection to a remote service. When doing so, the connection details take precedence over any connection-related configuration properties.spring-doc.cadn.net.cn

When using Testcontainers, connection details can be automatically created for a service running in a container by annotating the container field in the test class.spring-doc.cadn.net.cn

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;

@Testcontainers
@SpringBootTest
class MyIntegrationTests {

	@Container
	@ServiceConnection
	static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");

	@Test
	void myTest() {
		// ...
	}

}
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;

@Testcontainers
@SpringBootTest
class MyIntegrationTests {

	@Test
	fun myTest() {
		// ...
	}

	companion object {

		@Container
		@ServiceConnection
		@JvmStatic
		val neo4j = Neo4jContainer("neo4j:5");

	}

}

Thanks to @ServiceConnection, the above configuration allows Neo4j-related beans in the application to communicate with Neo4j running inside the Testcontainers-managed Docker container. This is done by automatically defining a Neo4jConnectionDetails bean which is then used by the Neo4j auto-configuration, overriding any connection-related configuration properties.spring-doc.cadn.net.cn

You’ll need to add the spring-boot-testcontainers module as a test dependency in order to use service connections with Testcontainers.

Service connection annotations are processed by ContainerConnectionDetailsFactory classes registered with spring.factories. A ContainerConnectionDetailsFactory can create a ConnectionDetails bean based on a specific Container subclass, or the Docker image name.spring-doc.cadn.net.cn

The following service connection factories are provided in the spring-boot-testcontainers jar:spring-doc.cadn.net.cn

Connection Details Matched on

ActiveMQConnectionDetailsspring-doc.cadn.net.cn

Containers named "symptoma/activemq" or ActiveMQContainerspring-doc.cadn.net.cn

ArtemisConnectionDetailsspring-doc.cadn.net.cn

Containers of type ArtemisContainerspring-doc.cadn.net.cn

CassandraConnectionDetailsspring-doc.cadn.net.cn

Containers of type CassandraContainerspring-doc.cadn.net.cn

CouchbaseConnectionDetailsspring-doc.cadn.net.cn

Containers of type CouchbaseContainerspring-doc.cadn.net.cn

ElasticsearchConnectionDetailsspring-doc.cadn.net.cn

Containers of type ElasticsearchContainerspring-doc.cadn.net.cn

FlywayConnectionDetailsspring-doc.cadn.net.cn

Containers of type JdbcDatabaseContainerspring-doc.cadn.net.cn

JdbcConnectionDetailsspring-doc.cadn.net.cn

Containers of type JdbcDatabaseContainerspring-doc.cadn.net.cn

KafkaConnectionDetailsspring-doc.cadn.net.cn

Containers of type KafkaContainer, ConfluentKafkaContainer or RedpandaContainerspring-doc.cadn.net.cn

LdapConnectionDetailsspring-doc.cadn.net.cn

Containers named "osixia/openldap" or of type LLdapContainerspring-doc.cadn.net.cn

LiquibaseConnectionDetailsspring-doc.cadn.net.cn

Containers of type JdbcDatabaseContainerspring-doc.cadn.net.cn

MongoConnectionDetailsspring-doc.cadn.net.cn

Containers of type MongoDBContainerspring-doc.cadn.net.cn

Neo4jConnectionDetailsspring-doc.cadn.net.cn

Containers of type Neo4jContainerspring-doc.cadn.net.cn

OtlpLoggingConnectionDetailsspring-doc.cadn.net.cn

Containers named "otel/opentelemetry-collector-contrib" or of type LgtmStackContainerspring-doc.cadn.net.cn

OtlpMetricsConnectionDetailsspring-doc.cadn.net.cn

Containers named "otel/opentelemetry-collector-contrib" or of type LgtmStackContainerspring-doc.cadn.net.cn

OtlpTracingConnectionDetailsspring-doc.cadn.net.cn

Containers named "otel/opentelemetry-collector-contrib" or of type LgtmStackContainerspring-doc.cadn.net.cn

PulsarConnectionDetailsspring-doc.cadn.net.cn

Containers of type PulsarContainerspring-doc.cadn.net.cn

R2dbcConnectionDetailsspring-doc.cadn.net.cn

Containers of type ClickHouseContainer, MariaDBContainer, MSSQLServerContainer, MySQLContainer, OracleContainer (free), OracleContainer (XE) or PostgreSQLContainerspring-doc.cadn.net.cn

RabbitConnectionDetailsspring-doc.cadn.net.cn

Containers of type RabbitMQContainerspring-doc.cadn.net.cn

RedisConnectionDetailsspring-doc.cadn.net.cn

Containers of type RedisContainer or RedisStackContainer, or containers named "redis", "redis/redis-stack" or "redis/redis-stack-server"spring-doc.cadn.net.cn

ZipkinConnectionDetailsspring-doc.cadn.net.cn

Containers named "openzipkin/zipkin"spring-doc.cadn.net.cn

By default all applicable connection details beans will be created for a given Container. For example, a PostgreSQLContainer will create both JdbcConnectionDetails and R2dbcConnectionDetails.spring-doc.cadn.net.cn

If you want to create only a subset of the applicable types, you can use the type attribute of @ServiceConnection.spring-doc.cadn.net.cn

By default Container.getDockerImageName().getRepository() is used to obtain the name used to find connection details. The repository portion of the Docker image name ignores any registry and the version. This works as long as Spring Boot is able to get the instance of the Container, which is the case when using a static field like in the example above.spring-doc.cadn.net.cn

If you’re using a @Bean method, Spring Boot won’t call the bean method to get the Docker image name, because this would cause eager initialization issues. Instead, the return type of the bean method is used to find out which connection detail should be used. This works as long as you’re using typed containers such as Neo4jContainer or RabbitMQContainer. This stops working if you’re using GenericContainer, for example with Redis as shown in the following example:spring-doc.cadn.net.cn

import org.testcontainers.containers.GenericContainer;

import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;

@TestConfiguration(proxyBeanMethods = false)
public class MyRedisConfiguration {

	@Bean
	@ServiceConnection(name = "redis")
	public GenericContainer<?> redisContainer() {
		return new GenericContainer<>("redis:7");
	}

}
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.boot.testcontainers.service.connection.ServiceConnection
import org.springframework.context.annotation.Bean
import org.testcontainers.containers.GenericContainer

@TestConfiguration(proxyBeanMethods = false)
class MyRedisConfiguration {
	@Bean
	@ServiceConnection(name = "redis")
	fun redisContainer(): GenericContainer<*> {
		return GenericContainer("redis:7")
	}
}

Spring Boot can’t tell from GenericContainer which container image is used, so the name attribute from @ServiceConnection must be used to provide that hint.spring-doc.cadn.net.cn

You can also use the name attribute of @ServiceConnection to override which connection detail will be used, for example when using custom images. If you are using the Docker image registry.mycompany.com/mirror/myredis, you’d use @ServiceConnection(name="redis") to ensure RedisConnectionDetails are created.spring-doc.cadn.net.cn

SSL with Service Connections

You can use the @Ssl, @JksKeyStore, @JksTrustStore, @PemKeyStore and @PemTrustStore annotations on a supported container to enable SSL support for that service connection. Please note that you still have to enable SSL on the service which is running inside the Testcontainer yourself, the annotations only configure SSL on the client side in your application.spring-doc.cadn.net.cn

import com.redis.testcontainers.RedisContainer;
import org.junit.jupiter.api.Test;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.PemKeyStore;
import org.springframework.boot.testcontainers.service.connection.PemTrustStore;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.data.redis.core.RedisOperations;

@Testcontainers
@SpringBootTest
class MyRedisWithSslIntegrationTests {

	@Container
	@ServiceConnection
	@PemKeyStore(certificate = "classpath:client.crt", privateKey = "classpath:client.key")
	@PemTrustStore("classpath:ca.crt")
	static RedisContainer redis = new SecureRedisContainer("redis:latest");

	@Autowired
	private RedisOperations<Object, Object> operations;

	@Test
	void testRedis() {
		// ...
	}

}

The above code uses the @PemKeyStore annotation to load the client certificate and key into the keystore and the and @PemTrustStore annotation to load the CA certificate into the truststore. This will authenticate the client against the server, and the CA certificate in the truststore makes sure that the server certificate is valid and trusted.spring-doc.cadn.net.cn

The SecureRedisContainer in this example is a custom subclass of RedisContainer which copies certificates to the correct places and invokes redis-server with commandline parameters enabling SSL.spring-doc.cadn.net.cn

The SSL annotations are supported for the following service connections:spring-doc.cadn.net.cn

The ElasticsearchContainer additionally supports automatic detection of server side SSL. To use this feature, annotate the container with @Ssl, as seen in the following example, and Spring Boot takes care of the client side SSL configuration for you:spring-doc.cadn.net.cn

import org.junit.jupiter.api.Test;
import org.testcontainers.elasticsearch.ElasticsearchContainer;
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.elasticsearch.DataElasticsearchTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.boot.testcontainers.service.connection.Ssl;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;

@Testcontainers
@DataElasticsearchTest
class MyElasticsearchWithSslIntegrationTests {

	@Ssl
	@Container
	@ServiceConnection
	static ElasticsearchContainer elasticsearch = new ElasticsearchContainer(
			"docker.elastic.co/elasticsearch/elasticsearch:8.17.2");

	@Autowired
	private ElasticsearchTemplate elasticsearchTemplate;

	@Test
	void testElasticsearch() {
		// ...
	}

}

Dynamic Properties

A slightly more verbose but also more flexible alternative to service connections is @DynamicPropertySource. A static @DynamicPropertySource method allows adding dynamic property values to the Spring Environment.spring-doc.cadn.net.cn

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;

@Testcontainers
@SpringBootTest
class MyIntegrationTests {

	@Container
	static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5");

	@Test
	void myTest() {
		// ...
	}

	@DynamicPropertySource
	static void neo4jProperties(DynamicPropertyRegistry registry) {
		registry.add("spring.neo4j.uri", neo4j::getBoltUrl);
	}

}
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import org.testcontainers.containers.Neo4jContainer
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers

@Testcontainers
@SpringBootTest
class MyIntegrationTests {

	@Test
	fun myTest() {
		// ...
	}

	companion object {
		@Container
		@JvmStatic
		val neo4j = Neo4jContainer("neo4j:5");

		@DynamicPropertySource
		@JvmStatic
		fun neo4jProperties(registry: DynamicPropertyRegistry) {
			registry.add("spring.neo4j.uri") { neo4j.boltUrl }
		}
	}
}

The above configuration allows Neo4j-related beans in the application to communicate with Neo4j running inside the Testcontainers-managed Docker container.spring-doc.cadn.net.cn