Skip to content

Commit 4a98b17

Browse files
thecooldropphilwebb
authored andcommitted
Document methods of starting Testcontainer containers
Update the reference documentation to provide more details about the three supported methods of starting Testcontainer containers. See gh-44187 Signed-off-by: Vanio Begic <vanio.begic123@gmail.com>
1 parent 2f242ca commit 4a98b17

File tree

9 files changed

+249
-1
lines changed

9 files changed

+249
-1
lines changed

buildSrc/src/main/resources/org/springframework/boot/build/antora/antora-asciidoc-attributes.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ url-spring-data-rest-docs=https://docs.spring.io/spring-data/rest/reference/{ant
7575
url-spring-data-rest-site=https://spring.io/projects/spring-data-rest
7676
url-spring-data-rest-javadoc=https://docs.spring.io/spring-data/rest/docs/{dotxversion-spring-data-rest}/api
7777
url-spring-data-site=https://spring.io/projects/spring-data
78+
url-testcontainers-java-doc=https://java.testcontainers.org
7879
url-testcontainers-activemq-javadoc=https://javadoc.io/doc/org.testcontainers/activemq/{version-testcontainers-activemq}
7980
url-testcontainers-cassandra-javadoc=https://javadoc.io/doc/org.testcontainers/cassandra/{version-testcontainers-cassandra}
8081
url-testcontainers-couchbase-javadoc=https://javadoc.io/doc/org.testcontainers/couchbase/{version-testcontainers-couchbase}

spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/testing/testcontainers.adoc

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,89 @@ The https://www.testcontainers.org/[Testcontainers] library provides a way to ma
55
It integrates with JUnit, allowing you to write a test class that can start up a container before any of the tests run.
66
Testcontainers is especially useful for writing integration tests that talk to a real backend service such as MySQL, MongoDB, Cassandra and others.
77

8+
In following sections we will describe some of the methods you can use to integrate Testcontainers with your tests.
9+
10+
11+
12+
[[testing.testcontainers.via-junit-extension]]
13+
== Using via @Testcontainers JUnit5 extension
14+
15+
The Testcontainers provides JUnit5 extensions, which can be used to manage containers in your tests.
16+
The extension is activated by applying the `@Testcontainers` annotation from Testcontainers to your test class.
17+
818
Testcontainers can be used in a Spring Boot test as follows:
919

1020
include-code::vanilla/MyIntegrationTests[]
1121

12-
This will start up a docker container running Neo4j (if Docker is running locally) before any of the tests are run.
22+
This will start up a Docker container running Neo4j (if Docker is running locally) before any of the tests are run.
1323
In most cases, you will need to configure the application to connect to the service running in the container.
1424

25+
In this case the lifecycle of the container instance is managed by Testcontainers, as described in official documentation.
26+
27+
28+
[[testing.testcontainers.via-spring-beans]]
29+
== Using via Spring managed beans
30+
31+
The containers provided by Testcontainers can be managed by Spring Boot as beans.
32+
This method of managing contains can be used in combination with javadoc:org.springframework.boot.testcontainers.service.connection.ServiceConnection[format=annotation].
33+
34+
To use Testcontainer contains as Spring beans we need to create a configuration class declaring the container as bean:
35+
36+
include-code::beandeclaration/BeanDeclarationConfig[]
37+
38+
then we can start the container by importing the configuration class in the test class:
39+
40+
include-code::beandeclaration/SpringTest[]
41+
42+
43+
[[testing.testcontainers.via-declaration-classes]]
44+
== Using via importing container declaration classes
45+
46+
A common pattern with Testcontainers is to declare the Container instances as static fields in an interface.
47+
For example the following interface `MyInterface` declares two containers, one named `mongo` of type MongoDB and another named `neo` of type Neo4j:
48+
49+
include-code::importcontainers/MyInterface[]
50+
51+
When you have containers declared in this way, then you can have these containers managed by Spring Boot as beans.
52+
All that is needed to do that is adding javadoc:org.springframework.boot.testcontainers.context.ImportTestcontainers[format=annotation] to your configuration class as in:
53+
54+
include-code::importcontainers/MyConfiguration[]
55+
56+
TIP: Using interfaces for declaring contains helps with reuse.
57+
When containers are declared in an interface, this can be reused in your javadoc:org.springframework.context.annotation.Configuration[format=annotation] classes and in test classes.
58+
59+
60+
[[test.testcontainers.container-lifecycle]]
61+
== Lifecycle of managed containers
62+
63+
If you have used the annotations and extensions provided by Testcontainers, then the lifecycle of container instances is managed by the Testcontainers.
64+
Please refer to the {url-testcontainres-java-doc}[Testcontainers official documentation] for the information about lifecycle of the containers, when managed by the Testcontainers.
65+
66+
When the containers are managed by Spring as beans, then the lifecycle is clearly defined by Spring.
67+
The container beans are created and started before the beans of other types are created.
68+
This process ensures that any beans, which rely on functionality provided by the containers, can use those functionalities.
69+
70+
The test containers can be started multiple times.
71+
Like any other beans the test containers are created and started once per application context managed by the TestContext Framework.
72+
For details about how TestContext framework manages the underlying application contexts and beans therein, please refer to the {url-spring-framework-docs}[official Spring documentation].
73+
74+
The container beans are stopped after the destruction of beans of other types.
75+
This ensures that any beans depending on the functionalities provided by the containers are cleaned up first.
76+
77+
TIP: When your application beans rely on functionality of containers, prefer configuring the containers as Spring beans.
78+
When containers are managed as Spring beans, then Spring framework ensures that upon start the container beans are started before any beans relying on them.
79+
On shutdown the application beans depending on container functionalities are cleaned up first, and only then are the containers shut down.
80+
81+
NOTE: Having containers managed by Testcontainers instead of as Spring beans provides no guarantee of order in which beans and containers will shutdown.
82+
It can happen that containers are shutdown before the beans relying on container functionality are cleaned up.
83+
This can lead to exceptions being thrown by client beans due to loss of connection for example.
84+
85+
The containers are stopped as part of the application shutdown process, managed by the TestContext framework.
86+
When the application context gets shutdown, the containers are shutdown as well.
87+
This usually happens after all tests using that specific cached application context have finished executing, but may happen earlier depending on the caching behavior configured in TestContext Framework.
88+
89+
It is important to note that a single test container instance can be, and often is, retained across execution of tests from multiple test classes.
90+
1591

1692

1793
[[testing.testcontainers.service-connections]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docs.testing.testcontainers.beandeclaration;
18+
19+
import org.testcontainers.containers.MongoDBContainer;
20+
import org.testcontainers.utility.DockerImageName;
21+
22+
import org.springframework.boot.test.context.TestConfiguration;
23+
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
24+
import org.springframework.context.annotation.Bean;
25+
26+
@TestConfiguration(proxyBeanMethods = false)
27+
class BeanDeclarationConfig {
28+
29+
@ServiceConnection
30+
@Bean
31+
MongoDBContainer container() {
32+
return new MongoDBContainer(DockerImageName.parse("mongo:latest"));
33+
}
34+
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docs.testing.testcontainers.beandeclaration;
18+
19+
import org.junit.jupiter.api.Test;
20+
import org.testcontainers.containers.MongoDBContainer;
21+
22+
import org.springframework.beans.factory.annotation.Autowired;
23+
import org.springframework.boot.test.context.SpringBootTest;
24+
import org.springframework.context.annotation.Import;
25+
26+
@SpringBootTest
27+
@Import(BeanDeclarationConfig.class)
28+
class SpringTest {
29+
30+
@Autowired
31+
private MongoDBContainer mongo;
32+
33+
@Test
34+
void doTest() {
35+
System.out.println("Mongo db is running: " + this.mongo.isRunning());
36+
}
37+
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docs.testing.testcontainers.importcontainers;
18+
19+
import org.springframework.boot.test.context.TestConfiguration;
20+
import org.springframework.boot.testcontainers.context.ImportTestcontainers;
21+
22+
@TestConfiguration(proxyBeanMethods = false)
23+
@ImportTestcontainers(MyInterface.class)
24+
class MyConfiguration {
25+
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.docs.testing.testcontainers.importcontainers;
18+
19+
import org.testcontainers.containers.MongoDBContainer;
20+
import org.testcontainers.containers.Neo4jContainer;
21+
import org.testcontainers.junit.jupiter.Container;
22+
23+
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
24+
25+
interface MyInterface {
26+
27+
@Container
28+
@ServiceConnection
29+
MongoDBContainer mongoContainer = new MongoDBContainer("mongo:5.0");
30+
31+
@Container
32+
@ServiceConnection
33+
Neo4jContainer<?> neo4jContainer = new Neo4jContainer<>("neo4j:5");
34+
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.springframework.boot.docs.testing.testcontainers.beandeclaration
2+
3+
import org.springframework.boot.test.context.TestConfiguration
4+
import org.springframework.boot.testcontainers.service.connection.ServiceConnection
5+
import org.springframework.context.annotation.Bean
6+
import org.testcontainers.containers.MongoDBContainer
7+
import org.testcontainers.utility.DockerImageName
8+
9+
10+
@TestConfiguration(proxyBeanMethods = false)
11+
internal class BeanDeclarationConfig {
12+
@ServiceConnection
13+
@Bean
14+
fun container(): MongoDBContainer {
15+
return MongoDBContainer(DockerImageName.parse("mongo:latest"))
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.springframework.boot.docs.testing.testcontainers.beandeclaration
2+
3+
import org.junit.jupiter.api.Test
4+
import org.springframework.beans.factory.annotation.Autowired
5+
import org.springframework.boot.test.context.SpringBootTest
6+
import org.springframework.context.annotation.Import
7+
import org.testcontainers.containers.MongoDBContainer
8+
9+
@SpringBootTest
10+
@Import(BeanDeclarationConfig::class)
11+
class SpringTest {
12+
@Autowired
13+
private val mongo: MongoDBContainer? = null
14+
15+
@Test
16+
fun doTest() {
17+
println("Mongo db is running: " + mongo!!.isRunning)
18+
}
19+
}

src/checkstyle/checkstyle-suppressions.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
<suppress files="spring-boot-configuration-processor[\\/]src[\\/]test[\\/]java[\\/]org[\\/]springframework[\\/]boot[\\/]configurationsample[\\/]" checks="SpringDeprecatedCheck"/>
8383
<suppress files="ImportTestcontainersTests\.java" checks="InterfaceIsType" />
8484
<suppress files="MyContainers\.java" checks="InterfaceIsType" />
85+
<suppress files="MyInterface\.java" checks="InterfaceIsType" />
8586
<suppress files="SpringBootBanner\.java" checks="SpringLeadingWhitespace" />
8687
<suppress files="LoadTimeWeaverAwareConsumerContainers\.java" checks="InterfaceIsType" />
8788
</suppressions>

0 commit comments

Comments
 (0)