1. 概述

本文将介绍如何结合 Flapdoodle 提供的嵌入式 MongoDB 方案与 Spring Boot,实现流畅的 MongoDB 集成测试。

MongoDB 是当前最流行的 NoSQL 文档型数据库之一。凭借出色的可扩展性、原生支持分片机制以及活跃的社区生态,它被许多开发者视为“首选 NoSQL 存储”。

与任何持久化技术一样,能否轻松地对数据库与应用其他模块的集成进行测试,至关重要。幸运的是,Spring Boot 为这类测试提供了非常友好的支持,配合嵌入式数据库,可以做到开箱即用。


2. Maven 依赖

首先,我们为 Spring Boot 项目配置 Maven 父工程。使用 spring-boot-starter-parent 后,无需手动为每个依赖声明版本号,简化依赖管理。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.0</version>
    <relativePath /> <!-- 从仓库解析父级 POM -->
</parent>

接着引入核心依赖:

  • Spring Data MongoDB 支持

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
    
  • 嵌入式 MongoDB(仅测试环境使用)

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

⚠️ 注意:de.flapdoodle.embed.mongo 仅用于测试,通过 <scope>test</scope> 限制其作用范围,上线时不会引入该依赖,无需额外关闭嵌入式数据库


3. 使用嵌入式 MongoDB 进行测试

本节涵盖两种常见场景:Spring Boot 自动配置测试 和 手动配置测试。

3.1. Spring Boot 测试(推荐)

添加 de.flapdoodle.embed.mongo 后,Spring Boot 会在运行测试时自动下载并启动嵌入式 MongoDB 实例。首次下载较慢,后续测试会复用本地实例,速度飞快。

以下是一个典型的 JUnit 5 集成测试示例:

@DataMongoTest
@ExtendWith(SpringExtension.class)
public class MongoDbSpringIntegrationTest {

    @DisplayName("给定对象,使用 MongoDB Template 保存,应成功存储")
    @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");
    }
}

效果:测试运行时,Spring 自动拉起嵌入式 MongoDB,控制台会输出类似:

...Starting MongoDbSpringIntegrationTest on localhost with PID 12345...

整个过程无需任何额外配置,简单粗暴,适合大多数场景。


3.2. 手动配置测试(高级用法)

虽然 Spring Boot 能自动配置,但某些情况下我们可能需要手动控制嵌入式 MongoDB 的版本、端口或网络配置,比如验证特定 MongoDB 版本的行为。

以下代码展示了如何手动启动嵌入式实例,并创建 MongoTemplate

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("给定对象,使用 MongoDB Template 保存,应成功存储")
    @Test
    void test() {
        // 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");
    }
}

📌 技巧:若想将手动创建的 MongoTemplate 注入 Spring 容器,可使用 @TestConfiguration 定义一个 @Bean 方法返回该实例,便于在多个测试中复用。

更多配置细节可参考 Flapdoodle 官方 GitHub 仓库


3.3. 日志控制

集成测试时,MongoDB 的日志可能会刷屏。可通过在 src/test/resources/application.properties 中调整日志级别来控制:

# 控制嵌入式 MongoDB 相关日志
logging.level.org.springframework.boot.autoconfigure.mongo.embedded=off
logging.level.org.mongodb=off

✅ 推荐测试时关闭这些日志,避免干扰输出。需要调试时再打开 DEBUG 级别。


3.4. 生产环境使用真实数据库

由于 de.flapdoodle.embed.mongo 被声明为 test 范围依赖,在生产环境中天然不会生效,无需额外配置开关。

只需在 application.yml 中指定真实 MongoDB 的连接信息即可:

spring:
  data:
    mongodb:
      uri: mongodb://prod-mongo-host:27017/myapp

📌 进阶建议:若想在不同环境(如本地开发、CI)灵活切换嵌入式与真实数据库,可使用 Spring Profile 配置不同的 MongoClient Bean,实现无缝切换。

例如:

  • @Profile("test"):使用嵌入式 MongoDB
  • @Profile("prod"):使用远程 MongoDB 实例

同时,生产环境的 MongoDB 驱动依赖应设置为 <scope>runtime</scope>,确保测试时不冲突。


4. 嵌入式测试的争议

嵌入式数据库看似完美,但也有明显局限。

适合场景

  • 验证实体与文档的映射(如 @Document 注解行为)
  • 测试自定义持久化事件监听器(如继承 AbstractMongoEventListener
  • 验证直接操作持久层的业务逻辑

不适合场景

  • 不能替代“全链路集成测试”。Flapdoodle 的嵌入式 MongoDB 并非官方 MongoDB 二进制,行为可能与生产环境存在细微差异。
  • 无法测试真实网络延迟、连接池行为、分片集群、副本集选举等高级特性。

📌 推荐方案:若需更贴近生产环境的测试,建议使用 Docker 启动真实 MongoDB 容器。通过 Testcontainers 等工具,可在 CI/CD 中动态管理容器生命周期,实现真正的端到端集成测试。

想了解 Docker 化 Spring Boot 应用?可参考我们之前的教程:《Dockerizing a Spring Boot Application》


5. 总结

Spring Boot 配合 Flapdoodle,让 MongoDB 集成测试变得极其简单。只需添加一个依赖,即可自动获得嵌入式数据库支持,快速验证数据层逻辑。

但务必记住:

⚠️ 嵌入式 MongoDB ≠ 生产级 MongoDB

它适合单元化、模块化的集成验证,但不能替代基于真实环境的集成测试。合理使用嵌入式数据库,结合 Docker 容器化方案,才能构建真正可靠的测试体系。

本文所有示例代码已托管至 GitHub:
👉 https://github.com/eugenp/tutorials/tree/master/persistence-modules/spring-boot-persistence-mongodb


原始标题:Spring Boot Integration Testing with Embedded MongoDB | Baeldung