Skip to content

Commit abe89ee

Browse files
committed
Merge branch '3.4.x'
Closes gh-45367
2 parents 9747d26 + 19ae4fb commit abe89ee

File tree

17 files changed

+356
-14
lines changed

17 files changed

+356
-14
lines changed

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

+1
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-docs=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

+85-4
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,93 @@ 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-
Testcontainers can be used in a Spring Boot test as follows:
8+
In following sections we will describe some of the methods you can use to integrate Testcontainers with your tests.
99

10-
include-code::vanilla/MyIntegrationTests[]
1110

12-
This will start up a docker container running Neo4j (if Docker is running locally) before any of the tests are run.
13-
In most cases, you will need to configure the application to connect to the service running in the container.
11+
[[testing.testcontainers.spring-beans]]
12+
== Using Spring Beans
13+
14+
The containers provided by Testcontainers can be managed by Spring Boot as beans.
15+
16+
To declare a container as a bean, add a javadoc:org.springframework.context.annotation.Bean[format=annotation] method to your test configuration:
17+
18+
include-code::MyTestConfiguration[]
19+
20+
You can then inject and use the container by importing the configuration class in the test class:
21+
22+
include-code::MyIntegrationTests[]
23+
24+
TIP: This method of managing containers is often used in combination with xref:#testing.testcontainers.service-connections[service connection annotations].
25+
26+
27+
28+
[[testing.testcontainers.junit-extension]]
29+
== Using the JUnit Extension
30+
31+
Testcontainers provides a JUnit extension which can be used to manage containers in your tests.
32+
The extension is activated by applying the javadoc:org.testcontainers.junit.jupiter.Testcontainers[format=annotation] annotation from Testcontainers to your test class.
33+
34+
You can then use the javadoc:org.testcontainers.junit.jupiter.Container[format=annotation] annotation on static container fields.
35+
36+
The javadoc:org.testcontainers.junit.jupiter.Testcontainers[format=annotation] annotation can be used on vanilla JUnit tests, or in combination with javadoc:org.springframework.boot.test.context.SpringBootTest[format=annotation]:
37+
38+
include-code::MyIntegrationTests[]
39+
40+
The example above will start up a Neo4j container before any of the tests are run.
41+
The lifecycle of the container instance is managed by Testcontainers, as described in {url-testcontainers-docs}/test_framework_integration/junit_5/#extension[their official documentation].
42+
43+
NOTE: In most cases, you will additionally need to configure the application to connect to the service running in the container.
44+
45+
46+
47+
[[testing.testcontainers.importing-configuration-interfaces]]
48+
== Importing Container Configuration Interfaces
49+
50+
A common pattern with Testcontainers is to declare the container instances as static fields in an interface.
51+
52+
For example, the following interface declares two containers, one named `mongo` of type javadoc:org.testcontainers.containers.MongoDBContainer[] and another named `neo4j` of type javadoc:org.testcontainers.containers.Neo4jContainer.Neo4jContainer[]:
53+
54+
include-code::MyContainers[]
55+
56+
When you have containers declared in this way, you can reuse their configuration in multiple tests by having the test classes implement the interface.
57+
58+
It's also possible to use the same interface configuration in your Spring Boot tests.
59+
To do so, add javadoc:org.springframework.boot.testcontainers.context.ImportTestcontainers[format=annotation] to your test configuration class:
60+
61+
include-code::MyTestConfiguration[]
62+
63+
64+
65+
[[testing.testcontainers.lifecycle]]
66+
== Lifecycle of Managed Containers
67+
68+
If you have used the annotations and extensions provided by Testcontainers, then the lifecycle of container instances is managed entirely by Testcontainers.
69+
Please refer to the {url-testcontainers-docs}[offical Testcontainers documentation] for the information.
70+
71+
When the containers are managed by Spring as beans, then their lifecycle is managed by Spring:
72+
73+
* Container beans are created and started before all other beans.
74+
75+
* Container beans are stopped after the destruction of all other beans.
76+
77+
This process ensures that any beans, which rely on functionality provided by the containers, can use those functionalities.
78+
It also ensures that they are cleaned up whilst the container is still available.
79+
80+
TIP: When your application beans rely on functionality of containers, prefer configuring the containers as Spring beans to ensure the correct lifecycle behavior.
81+
82+
NOTE: Having containers managed by Testcontainers instead of as Spring beans provides no guarantee of the order in which beans and containers will shutdown.
83+
It can happen that containers are shutdown before the beans relying on container functionality are cleaned up.
84+
This can lead to exceptions being thrown by client beans, for example, due to loss of connection.
85+
86+
Container beans are created and started once per application context managed by Spring's TestContext Framework.
87+
For details about how TestContext Framework manages the underlying application contexts and beans therein, please refer to the {url-spring-framework-docs}[Spring Framework documentation].
88+
89+
Container beans are stopped as part of the TestContext Framework's standard application context shutdown process.
90+
When the application context gets shutdown, the containers are shutdown as well.
91+
This usually happens after all tests using that specific cached application context have finished executing.
92+
It may also happen earlier, depending on the caching behavior configured in TestContext Framework.
93+
94+
NOTE: A single test container instance can, and often is, retained across execution of tests from multiple test classes.
1495

1596

1697

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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.importingconfigurationinterfaces;
18+
19+
import org.testcontainers.containers.MongoDBContainer;
20+
import org.testcontainers.containers.Neo4jContainer;
21+
import org.testcontainers.junit.jupiter.Container;
22+
23+
interface MyContainers {
24+
25+
@Container
26+
MongoDBContainer mongoContainer = new MongoDBContainer("mongo:5.0");
27+
28+
@Container
29+
Neo4jContainer<?> neo4jContainer = new Neo4jContainer<>("neo4j:5");
30+
31+
}
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.importingconfigurationinterfaces;
18+
19+
import org.springframework.boot.test.context.TestConfiguration;
20+
import org.springframework.boot.testcontainers.context.ImportTestcontainers;
21+
22+
@TestConfiguration(proxyBeanMethods = false)
23+
@ImportTestcontainers(MyContainers.class)
24+
class MyTestConfiguration {
25+
26+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.boot.docs.testing.testcontainers.vanilla;
17+
package org.springframework.boot.docs.testing.testcontainers.junitextension;
1818

1919
import org.junit.jupiter.api.Test;
2020
import org.testcontainers.containers.Neo4jContainer;
@@ -32,7 +32,7 @@ class MyIntegrationTests {
3232

3333
@Test
3434
void myTest() {
35-
// ...
35+
/**/ System.out.println(neo4j);
3636
}
3737

3838
}

spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/testing/testcontainers/serviceconnections/MyIntegrationTests.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ class MyIntegrationTests {
3434

3535
@Test
3636
void myTest() {
37-
// ...
37+
/**/ System.out.println(neo4j);
3838
}
3939

4040
}
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.springbeans;
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(MyTestConfiguration.class)
28+
class MyIntegrationTests {
29+
30+
@Autowired
31+
private MongoDBContainer mongo;
32+
33+
@Test
34+
void myTest() {
35+
/**/ System.out.println(this.mongo);
36+
}
37+
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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.springbeans;
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.context.annotation.Bean;
24+
25+
@TestConfiguration(proxyBeanMethods = false)
26+
class MyTestConfiguration {
27+
28+
@Bean
29+
MongoDBContainer mongoDbContainer() {
30+
return new MongoDBContainer(DockerImageName.parse("mongo:5.0"));
31+
}
32+
33+
}

spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/dynamicproperties/MyIntegrationTests.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class MyIntegrationTests {
2929

3030
@Test
3131
fun myTest() {
32-
// ...
32+
/**/ println()
3333
}
3434

3535
companion object {
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.importingconfigurationinterfaces
18+
19+
import org.testcontainers.containers.MongoDBContainer
20+
import org.testcontainers.containers.Neo4jContainer
21+
import org.testcontainers.junit.jupiter.Container
22+
23+
interface MyContainers {
24+
25+
companion object {
26+
27+
@Container
28+
val mongoContainer: MongoDBContainer = MongoDBContainer("mongo:5.0")
29+
30+
@Container
31+
val neo4jContainer: Neo4jContainer<*> = Neo4jContainer("neo4j:5")
32+
33+
}
34+
35+
}
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.importingconfigurationinterfaces
18+
19+
import org.springframework.boot.test.context.TestConfiguration
20+
import org.springframework.boot.testcontainers.context.ImportTestcontainers
21+
22+
@TestConfiguration(proxyBeanMethods = false)
23+
@ImportTestcontainers(MyContainers::class)
24+
class MyTestConfiguration {
25+
26+
}
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,30 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.boot.docs.testing.testcontainers.vanilla
17+
package org.springframework.boot.docs.testing.testcontainers.junitextension
1818

1919
import org.junit.jupiter.api.Test;
2020
import org.testcontainers.containers.Neo4jContainer;
2121
import org.testcontainers.junit.jupiter.Container;
2222
import org.testcontainers.junit.jupiter.Testcontainers;
2323

2424
import org.springframework.boot.test.context.SpringBootTest;
25-
import org.springframework.boot.testcontainers.service.connection.ServiceConnection
2625

2726
@Testcontainers
2827
@SpringBootTest
2928
class MyIntegrationTests {
3029

3130
@Test
3231
fun myTest() {
33-
// ...
32+
/**/ println()
3433
}
3534

3635
companion object {
36+
3737
@Container
3838
@JvmStatic
3939
val neo4j = Neo4jContainer("neo4j:5");
40+
4041
}
4142
}
4243

spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/testing/testcontainers/serviceconnections/MyIntegrationTests.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class MyIntegrationTests {
3030

3131
@Test
3232
fun myTest() {
33-
// ...
33+
/**/ println()
3434
}
3535

3636
companion object {

0 commit comments

Comments
 (0)