Spring Boot Integration Testing with Embedded MongoDB

1. Overview

In this tutorial, we’ll learn how to use Flapdoodle’s embedded MongoDB solution together with Spring Boot to run MongoDB integration tests smoothly.

MongoDB is a popular NoSQL document database. Thanks to the high scalability, built-in sharding and excellent community support it’s often considered “the NoSQL storage” by many developers.

As with any other persistence technology, it’s critical to be able to test database integration with the rest of our application easily. Thankfully, Spring Boot allows us to write that kind of tests easily.

2. Maven Dependencies

First, let’s set up the Maven parent for our Boot project.

Thanks to the parent we don’t need to define version for each Maven dependency manually.

We’re naturally going to be using Spring Boot:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.1</version>
    <relativePath /> <!-- lookup parent from repository -->
</parent>

You can find the latest Boot version here.

Since we added Spring Boot parent, we can add required dependencies without specifying their versions:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

spring-boot-starter-data-mongodb will enable Spring support for MongoDB:

<dependency>
    <groupId>de.flapdoodle.embed</groupId>
    <artifactId>de.flapdoodle.embed.mongo</artifactId>
    <scope>test</scope>
</dependency>

de.flapdoodle.embed.mongo provides embedded MongoDB for integration tests.

3. Test Using Embedded MongoDB

This section covers two scenarios: Spring Boot test and manual test.

3.1. Spring Boot Test

After adding de.flapdoodle.embed.mongo dependency Spring Boot will automatically try to download and start the embedded MongoDB when running tests.

The package will be downloaded only once for each version so that subsequent tests run much faster.

At this stage we should be able to start and pass the sample JUnit 5 integration test:

@DataMongoTest
@ExtendWith(SpringExtension.class)
public class MongoDbSpringIntegrationTest {
    @DisplayName("given object to save"
        + " when save object using MongoDB template"
        + " then object is saved")
    @Test
    public void test(@Autowired MongoTemplate mongoTemplate) {
        // given
        DBObject objectToSave = BasicDBObjectBuilder.start()
            .add("key", "value")
            .get();

        // when
        mongoTemplate.save(objectToSave, "collection");

        // then
        assertThat(mongoTemplate.findAll(DBObject.class, "collection")).extracting("key")
            .containsOnly("value");
    }
}

As we can see, the embedded database was automatically started by Spring, which should also be logged in the console:

...Starting MongodbExampleApplicationTests on arroyo with PID 10413...

3.2. Manual Configuration Test

Spring Boot will automatically start and configure the embedded database and then inject MongoTemplate instance for us. However, sometimes we might need to configure embedded Mongo database manually (e.g., when testing a specific DB version).

The following snippet shows how we can configure the embedded MongoDB instance manually. This is roughly the equivalent of the previous Spring test:

class ManualEmbeddedMongoDbIntegrationTest {
    private static final String CONNECTION_STRING = "mongodb://%s:%d";

    private MongodExecutable mongodExecutable;
    private MongoTemplate mongoTemplate;

    @AfterEach
    void clean() {
        mongodExecutable.stop();
    }

    @BeforeEach
    void setup() throws Exception {
        String ip = "localhost";
        int port = 27017;

        ImmutableMongodConfig mongodConfig = MongodConfig
            .builder()
            .version(Version.Main.PRODUCTION)
            .net(new Net(ip, port, Network.localhostIsIPv6()))
            .build();

        MongodStarter starter = MongodStarter.getDefaultInstance();
        mongodExecutable = starter.prepare(mongodConfig);
        mongodExecutable.start();
        mongoTemplate = new MongoTemplate(MongoClients.create(String.format(CONNECTION_STRING, ip, port)), "test");
    }

    @DisplayName("given object to save"
        + " when save object using MongoDB template"
        + " then object is saved")
    @Test
    void test() throws Exception {
        // given
        DBObject objectToSave = BasicDBObjectBuilder.start()
            .add("key", "value")
            .get();

        // when
        mongoTemplate.save(objectToSave, "collection");

        // then
        assertThat(mongoTemplate.findAll(DBObject.class, "collection")).extracting("key")
            .containsOnly("value");
    }
}

Note, that we can quickly create MongoTemplate bean configured to use our manually configured embedded database and register it inside the Spring container by merely creating, e.g., a @TestConfiguration with @Bean method that will return new MongoTemplate(MongoClients.create(connectionString, “test”).

More examples can be found on the official Flapdoodle’s GitHub repository.

3.3. Logging

We can configure logging messages for MongoDB when running integration tests by adding these two properties to src/test/resources/application.propertes file:

logging.level.org.springframework.boot.autoconfigure.mongo.embedded
logging.level.org.mongodb

For example, to disable logging, we simply set the values to off:

logging.level.org.springframework.boot.autoconfigure.mongo.embedded=off
logging.level.org.mongodb=off

3.4. Using a Real Database on Production

Since we added de.flapdoodle.embed.mongo dependency using <scope>test</scope> there’s no need to disable embedded database when running on production. All we have to do is to specify MongoDB connection details (e.g., host and port) and we are good to go.

To use an embedded DB outside of tests, we can use Spring profiles that will register the right MongoClient (embedded or production) depending on the active profile.

We’ll also need to change the scope of the production dependency to <scope>runtime</scope>.

4. Embedded Testing Controversy

Using embedded database might look like a great idea at the beginning. Indeed, it’s a good approach when we want to test if our application behaves correctly in areas such as:

  • Object<->Document mapping configuration
  • Custom persistence lifecycle event listeners (refer to AbstractMongoEventListener)
  • The logic of any code working directly with the persistence layer

Unfortunately, using an embedded server cannot be considered as “full integration testing”. Flapdoodle’s embedded MongoDB isn’t an official MongoDB product. Therefore, we cannot be sure that it behaves exactly as in the production environment.

If we want to run communication tests in the environment as close to the production as possible, a better solution is to use an environment container such as Docker.

5. Conclusion

Spring Boot makes it extremely simple to run tests that verify proper document mapping and database integration. By adding the right Maven dependency, we are immediately able to use MongoDB components in Spring Boot integration tests.

We need to remember that embedded MongoDB server cannot be considered a replacement for a “real” server.

The full source code of all the examples is available over on GitHub.

Related posts:

Java Program to Implement PriorityQueue API
Java Program to Implement Sieve Of Atkin
Java Program to Check if an UnDirected Graph is a Tree or Not Using DFS
Vòng lặp for, while, do-while trong Java
Hướng dẫn Java Design Pattern – Flyweight
Java Program to Perform Postorder Recursive Traversal of a Given Binary Tree
Java Program to Find Minimum Element in an Array using Linear Search
Explain about URL and HTTPS protocol
Hướng dẫn Java Design Pattern – Mediator
LinkedHashSet trong Java hoạt động như thế nào?
Loại bỏ các phần tử trùng trong một ArrayList như thế nào trong Java 8?
Java Program to Perform Sorting Using B-Tree
Cachable Static Assets with Spring MVC
Java Program to Implement Ternary Search Tree
Giới thiệu thư viện Apache Commons Chain
Java Program to Implement Sparse Array
Lập trình đa luồng trong Java (Java Multi-threading)
String Processing with Apache Commons Lang 3
Spring Boot - Runners
Java Program to Implement Skew Heap
Java – Convert File to InputStream
Java Program to Implement Binary Search Tree
Java Program to Generate All Possible Combinations of a Given List of Numbers
Tạo chương trình Java đầu tiên sử dụng Eclipse
Java Program to Implement the Bin Packing Algorithm
Java Program to Solve Travelling Salesman Problem for Unweighted Graph
Java Program to Implement the Binary Counting Method to Generate Subsets of a Set
Using the Map.Entry Java Class
An Intro to Spring Cloud Task
An Example of Load Balancing with Zuul and Eureka
Spring REST API + OAuth2 + Angular
Java Program to Find k Numbers Closest to Median of S, Where S is a Set of n Numbers