Spring, Spock and MongoDB in Testcontainers
Problems with linking MongoDB via Testcontainers with @SpringBootTest
When you write tests that run SpringBoot context and you want to use MongoDB instance provided by Testcontainers you have to somehow pass the right URI or a pair of host and port into the Context to be able to connect your framework to MongoDB properly.
There are a few ways of doing that, some are well documented on the Testcontainers page right here: https://www.testcontainers.org
It might be a little bit complicated to find the right setup, that would be able to intercept startup of the Spring Context in a right time providing both host and port of the MongoDB container and inject it into the framework, especially when you use Spock Test Framework (JUnit way seems to be documented a bit better).
First solution — use FixedHostPortGenericContainer (not highly recommended)
Testcontainers allows you to declare your containers in a few ways, below I present two of them.
// direct implementation
final MongoDBContainer mongoDBContainer = new MongoDBContainer(DockerImageName.parse("mongo:4.0.10"))// generic implementation
final MongoDBContainer mongoDBContainer = new FixedHostPortGenericContainer("mongo:4.0.10");
Both versions should work. The difference is MongoDBContainer will run on a random port while with FixedHostPortGenericContainer you can use a port of your choosing. It simplifies connecting to Mongo a lot, cause you can enforce a good old localhost:27017 this way, so no dynamic configurations would be required.
The problem with this solution is it does not work well if you have multiple tests suits using non-shared container running at once. Processes will often kill MongoDB container in their teardown phase when other suits are already running or will try to start their Mongo and find out the port is already taken.
Second solution — use dynamic host and port
// pom.xml dependencies<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency><dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mongodb</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>spock</artifactId>
<scope>test</scope>
</dependency>// SampleSuite.groovy@Testcontainers
@SpringBootTest
class SampleSuite extends Specification {
@Shared
static MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:4.0.18-xenial")
@DynamicPropertySource
static void mongoProps(DynamicPropertyRegistry registry) {
mongoDBContainer.start()
registry.add("spring.data.mongodb.uri", () -> mongoDBContainer.replicaSetUrl)
} void cleanup() {
// cleaning up a bit should be a good idea...
mongoDBContainer.stop()
}
}
…and that should be it. It will create the shared container instance for your tests in the SampleSuite and dynamically inject the Mongo URI into the Spring Context.
I hope it will help you get your setup right. Happy coding!