Spring Boot + DataMongoTest + Testcontainers: Testing MongoDB Repositories with a MongoDB Container

Introduction

The @DataMongoTest annotation in Spring Boot is designed for isolated testing of MongoDB repositories. By default, it configures an embedded MongoDB (Flapdoodle) for testing. However, in real-world scenarios, we might need to test against a real MongoDB instance without installing it locally.

This is where Testcontainers comes in.

What is Testcontainers?

Testcontainers is a Java library that allows running lightweight, disposable containers for databases, message queues, and other dependencies. It is particularly useful for integration testing as it provides:

✅ Real database testing without requiring local installation.
✅ Fresh database instances for each test execution.
✅ Automatic cleanup of containers after tests.
✅ Consistent testing environments across different machines.

Project Setup

To get started, create a Spring Boot project with the following dependencies:

  • Spring Boot Starter Data MongoDB – For working with MongoDB.
  • Spring Boot Starter Test – Provides JUnit, AssertJ, and Mockito.
  • Testcontainers MongoDB – Runs MongoDB in a container.

 

Spring Boot Testing Libraries

The spring-boot-starter-test dependency provides several useful testing tools:

  • JUnit – The core testing framework.
  • Spring Boot Test – Provides utilities for integration testing.
  • AssertJ – Fluent assertion library.
  • Mockito – A mocking framework.
  • Testcontainers – Allows running a real MongoDB database in a container.

Implementation

1. pom.xml Configuration

To use Testcontainers with MongoDB, add the necessary dependencies to pom.xml.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>tech.devblueprint</groupId>
<artifactId>datamongodb-testcontainers-spring-boot-testing-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>datamongodb-testcontainers-spring-boot-testing-example</name>
<description>Demo project for Spring Boot</description>
<url/>
<properties>
<java.version>17</java.version>
</properties>
<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.springframework.boot</groupId>
<artifactId>spring-boot-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mongodb</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.4.3</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>tech.devblueprint</groupId> <artifactId>datamongodb-testcontainers-spring-boot-testing-example</artifactId> <version>0.0.1-SNAPSHOT</version> <name>datamongodb-testcontainers-spring-boot-testing-example</name> <description>Demo project for Spring Boot</description> <url/> <properties> <java.version>17</java.version> </properties> <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.springframework.boot</groupId> <artifactId>spring-boot-testcontainers</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>junit-jupiter</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>mongodb</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>tech.devblueprint</groupId>
    <artifactId>datamongodb-testcontainers-spring-boot-testing-example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>datamongodb-testcontainers-spring-boot-testing-example</name>
    <description>Demo project for Spring Boot</description>
    <url/>
    <properties>
        <java.version>17</java.version>
    </properties>
    <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.springframework.boot</groupId>
            <artifactId>spring-boot-testcontainers</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>mongodb</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Key Dependencies Explained:

  • spring-boot-starter-data-mongodb – Enables MongoDB support.
  • spring-boot-starter-test – Includes essential testing libraries.
  • spring-boot-testcontainers – Enables Testcontainers integration with Spring Boot.
  • org.testcontainers:mongodb – Runs MongoDB in a container.
  • org.testcontainers:junit-jupiter – Integrates JUnit 5 with Testcontainers.

2. Configuring MongoDB Testcontainer

By default, @DataMongoTest starts an embedded MongoDB (Flapdoodle). To use a MongoDB container, we need to:

  1. Start a MongoDB Testcontainer in the test class.
  2. Dynamically provide the MongoDB URI to Spring Boot.

3. Document Definition

File: src/main/java/…/document/Device.java

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package tech.devblueprint.datamongodb_spring_boot_testing_example.document;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "devices")
public class Device {
@Id
private String id;
private String name;
private String type;
private String manufacturer;
// Default constructor
public Device() {
}
// Parameterized constructor
public Device(String id, String name, String type, String manufacturer) {
this.id = id;
this.name = name;
this.type = type;
this.manufacturer = manufacturer;
}
// Getter and Setter for id
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
// Getter and Setter for name
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// Getter and Setter for type
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
// Getter and Setter for manufacturer
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
}
package tech.devblueprint.datamongodb_spring_boot_testing_example.document; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; @Document(collection = "devices") public class Device { @Id private String id; private String name; private String type; private String manufacturer; // Default constructor public Device() { } // Parameterized constructor public Device(String id, String name, String type, String manufacturer) { this.id = id; this.name = name; this.type = type; this.manufacturer = manufacturer; } // Getter and Setter for id public String getId() { return id; } public void setId(String id) { this.id = id; } // Getter and Setter for name public String getName() { return name; } public void setName(String name) { this.name = name; } // Getter and Setter for type public String getType() { return type; } public void setType(String type) { this.type = type; } // Getter and Setter for manufacturer public String getManufacturer() { return manufacturer; } public void setManufacturer(String manufacturer) { this.manufacturer = manufacturer; } }
package tech.devblueprint.datamongodb_spring_boot_testing_example.document;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "devices")
public class Device {

    @Id
    private String id;
    private String name;
    private String type;
    private String manufacturer;

    // Default constructor
    public Device() {
    }

    // Parameterized constructor
    public Device(String id, String name, String type, String manufacturer) {
        this.id = id;
        this.name = name;
        this.type = type;
        this.manufacturer = manufacturer;
    }

    // Getter and Setter for id
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }

    // Getter and Setter for name
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    // Getter and Setter for type
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }

    // Getter and Setter for manufacturer
    public String getManufacturer() {
        return manufacturer;
    }
    public void setManufacturer(String manufacturer) {
        this.manufacturer = manufacturer;
    }
}

4. Repository Interface

File: src/main/java/…/repository/DeviceRepository.java

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package tech.devblueprint.datamongodb_spring_boot_testing_example.repository;
import tech.devblueprint.datamongodb_spring_boot_testing_example.document.Device;
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.Optional;
public interface DeviceRepository extends MongoRepository<Device, String> {
// Find a device by its name
Optional<Device> findByName(String name);
package tech.devblueprint.datamongodb_spring_boot_testing_example.repository; import tech.devblueprint.datamongodb_spring_boot_testing_example.document.Device; import org.springframework.data.mongodb.repository.MongoRepository; import java.util.Optional; public interface DeviceRepository extends MongoRepository<Device, String> { // Find a device by its name Optional<Device> findByName(String name);
package tech.devblueprint.datamongodb_spring_boot_testing_example.repository;

import tech.devblueprint.datamongodb_spring_boot_testing_example.document.Device;
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.Optional;

public interface DeviceRepository extends MongoRepository<Device, String> {

    // Find a device by its name
    Optional<Device> findByName(String name);

Writing Tests with Testcontainers

Using a MongoDB Container in Tests

With @DataMongoTest, we can start a MongoDB container and register its connection details dynamically.

File: src/test/java/…/repository/DeviceRepositoryTestContainersTest.java

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package tech.devblueprint.datamongodb_testcontainers_spring_boot_testing_example;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.MongoDBContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import tech.devblueprint.datamongodb_testcontainers_spring_boot_testing_example.document.Device;
import tech.devblueprint.datamongodb_testcontainers_spring_boot_testing_example.repository.DeviceRepository;
import static org.assertj.core.api.Assertions.assertThat;
@Testcontainers
@DataMongoTest
public class DeviceRepositoryTestContainersTest {
// Start a MongoDB container with the latest image
@Container
static MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:latest");
// Dynamically set the Spring property for MongoDB URI
@DynamicPropertySource
static void setProperties(DynamicPropertyRegistry registry) {
// This provides the connection string to Spring Boot
registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl);
}
@Autowired
private DeviceRepository deviceRepository;
@BeforeEach
void setUp() {
// Clean up database and insert test data
deviceRepository.deleteAll();
Device device = new Device("20", "TCDevice", "Controller", "TCManufacturer");
deviceRepository.save(device);
}
@Test
void findByName_ReturnsDevice() {
// Test retrieval of device by name from MongoDB container
Device device = deviceRepository.findByName("TCDevice").orElse(null);
assertThat(device).isNotNull();
assertThat(device.getType()).isEqualTo("Controller");
assertThat(device.getManufacturer()).isEqualTo("TCManufacturer");
}
}
package tech.devblueprint.datamongodb_testcontainers_spring_boot_testing_example; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.testcontainers.containers.MongoDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import tech.devblueprint.datamongodb_testcontainers_spring_boot_testing_example.document.Device; import tech.devblueprint.datamongodb_testcontainers_spring_boot_testing_example.repository.DeviceRepository; import static org.assertj.core.api.Assertions.assertThat; @Testcontainers @DataMongoTest public class DeviceRepositoryTestContainersTest { // Start a MongoDB container with the latest image @Container static MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:latest"); // Dynamically set the Spring property for MongoDB URI @DynamicPropertySource static void setProperties(DynamicPropertyRegistry registry) { // This provides the connection string to Spring Boot registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl); } @Autowired private DeviceRepository deviceRepository; @BeforeEach void setUp() { // Clean up database and insert test data deviceRepository.deleteAll(); Device device = new Device("20", "TCDevice", "Controller", "TCManufacturer"); deviceRepository.save(device); } @Test void findByName_ReturnsDevice() { // Test retrieval of device by name from MongoDB container Device device = deviceRepository.findByName("TCDevice").orElse(null); assertThat(device).isNotNull(); assertThat(device.getType()).isEqualTo("Controller"); assertThat(device.getManufacturer()).isEqualTo("TCManufacturer"); } }
package tech.devblueprint.datamongodb_testcontainers_spring_boot_testing_example;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.MongoDBContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import tech.devblueprint.datamongodb_testcontainers_spring_boot_testing_example.document.Device;
import tech.devblueprint.datamongodb_testcontainers_spring_boot_testing_example.repository.DeviceRepository;

import static org.assertj.core.api.Assertions.assertThat;

@Testcontainers
@DataMongoTest
public class DeviceRepositoryTestContainersTest {

    // Start a MongoDB container with the latest image
    @Container
    static MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:latest");

    // Dynamically set the Spring property for MongoDB URI
    @DynamicPropertySource
    static void setProperties(DynamicPropertyRegistry registry) {
        // This provides the connection string to Spring Boot
        registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl);
    }

    @Autowired
    private DeviceRepository deviceRepository;

    @BeforeEach
    void setUp() {
        // Clean up database and insert test data
        deviceRepository.deleteAll();
        Device device = new Device("20", "TCDevice", "Controller", "TCManufacturer");
        deviceRepository.save(device);
    }

    @Test
    void findByName_ReturnsDevice() {
        // Test retrieval of device by name from MongoDB container
        Device device = deviceRepository.findByName("TCDevice").orElse(null);
        assertThat(device).isNotNull();
        assertThat(device.getType()).isEqualTo("Controller");
        assertThat(device.getManufacturer()).isEqualTo("TCManufacturer");
    }
}

Key Annotations Explained:

  • @Testcontainers – Enables Testcontainers integration.
  • @Container – Defines a MongoDB container that runs for the duration of the test class.
  • @DynamicPropertySource – Registers the container’s MongoDB URI dynamically.
  • @DataMongoTest – Loads only MongoDB-related components for testing.
  • @BeforeEach – Cleans up the database and inserts test data before each test.
  • @Test – Runs the test case for MongoDB repository methods.

Understanding Transactions in @DataMongoTest

Unlike relational databases, MongoDB does not support transactions in the same way as SQL databases. Therefore, @DataMongoTest does not roll back changes automatically.

If needed, you should manually clean up the database before each test:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@BeforeEach
void setUp() {
deviceRepository.deleteAll();
}
@BeforeEach void setUp() { deviceRepository.deleteAll(); }
@BeforeEach
void setUp() {
    deviceRepository.deleteAll();
}

Conclusion

In this guide, we explored how to use @DataMongoTest with Testcontainers to test MongoDB repositories with a real database instance.

Key Takeaways:

✅ Testcontainers provides a real MongoDB instance without requiring local installation.
✅ Automatically starts and stops a MongoDB database for testing.
✅ Dynamically injects database connection properties into Spring Boot.

By following this approach, you can test MongoDB repositories with a real database while keeping your tests fast, isolated, and disposable.