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 ConcurrentLinkedQueue API
Java Program to implement Circular Buffer
String Operations with Java Streams
Java Program to Implement Maximum Length Chain of Pairs
Java Program to Find the Number of Ways to Write a Number as the Sum of Numbers Smaller than Itself
Lớp Collections trong Java (Collections Utility Class)
Java Program to Create a Minimal Set of All Edges Whose Addition will Convert it to a Strongly Conne...
Java TreeMap vs HashMap
Java – Delete a File
Hướng dẫn sử dụng biểu thức chính quy (Regular Expression) trong Java
HashMap trong Java hoạt động như thế nào?
Tìm hiểu về Web Service
Spring Data Java 8 Support
Getting Started with Custom Deserialization in Jackson
Setting a Request Timeout for a Spring REST API
Java Program to Implement Gauss Seidel Method
Java Program to Generate N Number of Passwords of Length M Each
Java Program to Implement TreeMap API
Java Program to Check Whether it is Weakly Connected or Strongly Connected for a Directed Graph
Custom Exception trong Java
LinkedHashSet trong java
Giới thiệu SOAP UI và thực hiện test Web Service
Hướng dẫn tạo và sử dụng ThreadPool trong Java
Functional Interfaces in Java 8
Java Program to Implement Binary Tree
Check If Two Lists are Equal in Java
Toán tử trong java
Java Program to Perform Encoding of a Message Using Matrix Multiplication
Java Program to Implement Miller Rabin Primality Test Algorithm
Java Program to Find Number of Articulation points in a Graph
Tips for dealing with HTTP-related problems
An Intro to Spring Cloud Zookeeper