When you’re building Spring Boot applications that interact with MongoDB, ensuring the reliability of your data access layer is crucial. Unit tests play a vital role, but setting up and tearing down a real MongoDB instance for each test can be slow and cumbersome. This is where MongoDB Memory Server comes to the rescue, providing a fast and efficient in-memory alternative.
What is MongoDB Memory Server?
MongoDB Memory Server is a tool that spins up an actual MongoDB server process in memory. This gives you several key advantages:
- Real MongoDB Behavior: Unlike mocking, you’re interacting with a real MongoDB instance, which significantly reduces the risk of tests passing but failing in production due to subtle differences in behavior.
- Speed: In-memory operations are incredibly fast, leading to quicker test execution and faster development cycles.
- Isolation: Each test can have its own isolated MongoDB instance, preventing data contamination and ensuring reliable results.
How to Use MongoDB Memory Server in Your Spring Boot Tests
Here’s a step-by-step guide to integrating MongoDB Memory Server into your Spring Boot unit testing workflow:
1. Dependencies
- You’ll need the
de.flapdoodle.embed.mongo
dependency to manage the embedded MongoDB server. Add this to yourpom.xml
(Maven) orbuild.gradle
(Gradle):
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
// Gradle
testImplementation 'de.flapdoodle.embed:de.flapdoodle.embed.mongo'
2. Configuration (Spring Boot)
-
While MongoDB Memory Server handles the server itself, Spring Boot still needs database connection details. You can configure this in your
application.properties
orapplication.yml
withinsrc/test/resources
. -
Important: You won’t specify a fixed host and port here, as MongoDB Memory Server provides a dynamic connection URI.
-
Example (
application.properties
):
spring.data.mongodb.uri=${spring.mongodb.embedded.connection-string}
spring.data.mongodb.database=testdb
- Example (
application.yml
):
spring:
data:
mongodb:
uri: ${spring.mongodb.embedded.connection-string}
database: testdb
3. Setting Up and Tearing Down the Embedded MongoDB
- Here’s how you’d typically manage the MongoDB Memory Server lifecycle within your tests (using JUnit 5):
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import de.flapdoodle.embed.mongo.MongodExecutable;
import de.flapdoodle.embed.mongo.MongodStarter;
import de.flapdoodle.embed.mongo.config.ImmutableMongodConfig;
import de.flapdoodle.embed.mongo.config.Net;
import org.bson.Document;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.context.annotation.Import;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DataMongoTest
public class MyMongoDbTest {
private static MongodExecutable mongodExecutable;
private MongoClient mongoClient;
private MongoCollection<Document> collection;
@Autowired
private MongoTemplate mongoTemplate;
@Value("${spring.data.mongodb.database}")
private String databaseName;
@DynamicPropertySource
static void setProperties(DynamicPropertyRegistry registry) throws IOException {
MongodStarter starter = MongodStarter.getDefaultInstance();
int port = 27018; // Choose a free port
ImmutableMongodConfig mongodConfig = ImmutableMongodConfig.builder()
.net(new Net("localhost", port, false))
.build();
mongodExecutable = starter.prepare(mongodConfig);
mongodExecutable.start();
registry.add("spring.mongodb.embedded.connection-string", () -> "mongodb://localhost:" + port);
}
@BeforeEach
void setUp() {
mongoClient = mongoTemplate.getMongoDatabaseFactory().getMongoClient();
MongoDatabase database = mongoClient.getDatabase(databaseName);
collection = database.getCollection("mycollection");
collection.insertOne(new Document("name", "Test"));
}
@AfterEach
void tearDown() {
collection.deleteMany(new Document());
mongoClient.close();
mongodExecutable.stop();
}
@Test
void myTest() {
assertEquals(1, collection.countDocuments());
}
}
- Explanation
@DataMongoTest
: This Spring Boot annotation sets up a test context focused on MongoDB, similar to@DataJpaTest
for JPA.@DynamicPropertySource
: This annotation is used to dynamically set thespring.mongodb.embedded.connection-string
property. ThesetProperties
method starts the embedded MongoDB server and registers the connection string with Spring’s environment.MongodStarter
: This class from Flapdoodle is used to configure and start the MongoDB Memory Server.@BeforeEach
: This JUnit 5 annotation ensures that thesetUp
method is executed before each test method. Here, we get theMongoClient
from Spring’sMongoTemplate
, get the database and collection and insert a test document.@AfterEach
: This JUnit 5 annotation ensures that thetearDown
method is executed after each test method. Here, we delete all documents from the collection, close theMongoClient
and stop the embedded MongoDB server.mongoTemplate
: Spring Data MongoDB’sMongoTemplate
is used to interact with the database.
4. Writing Your Tests
- Now you can write your unit tests as you normally would, using Spring Data MongoDB’s repositories or the
MongoTemplate
to interact with the database.
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.data.mongodb.core.MongoTemplate;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DataMongoTest
public class MyServiceTest {
@Autowired
private MongoTemplate mongoTemplate;
@Test
void testMyServiceLogic() {
// Arrange
mongoTemplate.save(new MyDocument("testId", "testValue"), "mycollection");
// Act
MyService service = new MyService(mongoTemplate);
MyDocument result = service.getDocument("testId");
// Assert
assertEquals("testValue", result.getValue());
}
}
Benefits of Using MongoDB Memory Server
- Fast and Reliable Tests: Significantly speeds up test execution and provides consistent results.
- Reduced Setup Complexity: Automates the management of MongoDB instances for testing.
- Improved Code Quality: Encourages more thorough testing of your data access logic.
- Seamless Spring Boot Integration: Works well with Spring Data MongoDB and Spring Boot’s testing features.
Important Considerations
- Resource Consumption: Running a MongoDB process in memory can consume significant RAM, especially for large datasets.
- Platform Compatibility: Ensure that the embedded MongoDB binaries are available for your target platforms.
- Test Isolation: While MongoDB Memory Server provides good isolation, always clean up your test data properly to avoid any potential side effects.
Conclusion
MongoDB Memory Server is a valuable tool for any Spring Boot developer working with MongoDB. By providing a fast, reliable, and in-memory database for unit testing, it helps you write more effective tests and build higher-quality applications. Remember to carefully manage the lifecycle of the embedded MongoDB instance and consider the potential resource consumption in your test environment.
Discover more from GhostProgrammer - Jeff Miller
Subscribe to get the latest posts sent to your email.