<

I just announced the new Spring 5 modules in REST With Spring:

>> CHECK OUT THE LESSONS

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 setup 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.0.3.RELEASE</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 MongodExecutable mongodExecutable;
    private MongoTemplate mongoTemplate;

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

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

        IMongodConfig mongodConfig = new MongodConfigBuilder().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(new MongoClient(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(new MongoClient(bindIp, port), “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.

To find out more about Docker, read our previous article here.

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.

I just announced the new Spring 5 modules in REST With Spring:

>> CHECK OUT THE COURSE

Leave a Reply

avatar
  Subscribe  
Notify of