【问题标题】:Embedded MongoDB when running integration tests运行集成测试时嵌入 MongoDB
【发布时间】:2011-09-20 04:51:00
【问题描述】:

我的问题是this one 的变体。

由于我的 Java Web 应用程序项目需要大量读取过滤器/查询以及与 GridFS 等工具的接口,因此我正在努力想出一种以上述解决方案建议的方式使用 MongoDB 的明智方法。

因此,我正在考虑在集成测试的同时运行一个嵌入式 MongoDB 实例。我希望它自动启动(对于每个测试或整个套件),刷新数据库对于每个测试,然后关闭 最后。这些测试可能会在开发机器和 CI 服务器上运行,所以我的解决方案也需要便携

任何对 MongoDB 有更多了解的人都可以帮助我了解这种方法的可行性,和/或建议任何可以帮助我入门的阅读材料吗?

我也愿意接受人们可能提出的关于如何解决这个问题的其他建议...

【问题讨论】:

标签: java mongodb junit integration-testing embedded-database


【解决方案1】:

我找到了Embedded MongoDB 库,它看起来很有前途,可以满足您的要求。

当前支持 MongoDB 版本:1.6.53.1.6,前提是二进制文件仍可从配置的镜像中获得。

这是一个简短的使用示例,我刚刚尝试过,效果很好:

public class EmbeddedMongoTest {
    private static final String DATABASE_NAME = "embedded";

    private MongodExecutable mongodExe;
    private MongodProcess mongod;
    private Mongo mongo;

    @Before
    public void beforeEach() throws Exception {
        MongoDBRuntime runtime = MongoDBRuntime.getDefaultInstance();
        mongodExe = runtime.prepare(new MongodConfig(Version.V2_3_0, 12345, Network.localhostIsIPv6()));
        mongod = mongodExe.start();
        mongo = new Mongo("localhost", 12345);
    }

    @After
    public void afterEach() throws Exception {
        if (this.mongod != null) {
            this.mongod.stop();
            this.mongodExe.stop();
        }
    }

    @Test
    public void shouldCreateNewObjectInEmbeddedMongoDb() {
        // given
        DB db = mongo.getDB(DATABASE_NAME);
        DBCollection col = db.createCollection("testCollection", new BasicDBObject());

        // when
        col.save(new BasicDBObject("testDoc", new Date()));

        // then
        assertThat(col.getCount(), Matchers.is(1L));
    }
}

【讨论】:

  • 刚刚使用了这个库,它在 Mac 上完美地运行了 JUnit 测试 Mongo API。推荐。
  • +1 很棒的发现!一年前我第一次开始使用 mongodb 时,没有针对数据库进行测试的编程方式是缺点之一。我们通过在每个环境中都有一个测试实例来解决这个问题,通过 Java 属性文件进行配置,但当然需要在每个环境中安装 mongo。这看起来会解决所有问题。
  • 不错!删除了我的答案,因为它不再准确。有人知道这有多成熟吗?我可以想象它必须在非常低的级别上模拟 MongoDB 会非常复杂,并且从源代码来看它看起来非常高级。
  • 终于在我的项目中使用了这个,并且可以报告它非常容易设置和运行。底层调用都是官方com.mongodbJava API的一部分,所以并不比使用常规API复杂。
  • 小心这个解决方案。它只是收集有关当前操作系统的信息并从 Internet 下载适当的特定于平台的 MongoDB 二进制文件,运行守护程序并执行一些其他配置工作。作为企业解决方案,事实并非如此。模拟可能是唯一真正的选择。
【解决方案2】:

如果您使用的是 Maven,您可能会对我创建的包含 flapdoodle.de 'embedded mongo' API 的插件感兴趣:

embedmongo-maven-plugin

它提供了一个start 目标,您可以使用它来启动您想要的任何版本的MongoDB(例如在pre-integration-test 期间),以及一个将停止MongoDB 的stop 目标(例如在post-integration-test 期间)。

与其他插件相比,使用此插件的真正好处是不需要事先安装 MongoDB。下载 MongoDB 二进制文件并将其存储在 ~/.embedmongo 以供将来构建。

【讨论】:

【解决方案3】:

如果您使用的是 sbt 和 specs2,我为 embedmongo 编写了相同类型的包装器

https://github.com/athieriot/specs2-embedmongo

【讨论】:

    【解决方案4】:

    有 Foursquare 产品Fongo。 Fongo 是 mongo 的内存中 java 实现。它拦截对标准 mongo-java-driver 的调用,用于查找、更新、插入、删除和其他方法。主要用于您不想启动 mongo 进程的轻量级单元测试。

    【讨论】:

    • Fongo 是否会拦截对网络的调用,例如到 localhost:27017 以便它可以充当插入式假服务器以启用集成测试而无需更改代码?
    • mongo-java-server 是一个插入式假服务器实现,无需更改代码即可用于集成测试。
    【解决方案5】:

    在生产中,您将使用真实的数据库。

    如果您希望您的测试反映您的产品在生产中的行为方式,请使用 Mongo 的真实实例。

    伪造的实现可能与真实的不完全相同。在测试时,您应该争取正确性。执行速度排在第二位。

    【讨论】:

    • 我认为你错过了我的目的。我不是在寻找一个假的 Mongo 实例,我想要一个真实的实例,但嵌入到我的测试中。原因是启动 MongoDB 并将其置于特定状态而不会污染现有数据库,运行一系列操作,然后检查结果,而无需筛选与我的测试无关的任意数据。在保持受控测试环境的同时尽可能真实。
    • 对不起,“模拟”这个词和所有这些“内存中”的建议让我忘记了“嵌入”在 Java 领域的含义。很高兴听到它。
    【解决方案6】:

    使用 spring-boot 1.3 你可以使用 EmbeddedMongoAutoConfiguration

    pom.xml

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.2.RELEASE</version>
    </parent>
     ...
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>de.flapdoodle.embed</groupId>
            <artifactId>de.flapdoodle.embed.mongo</artifactId>
            <version>${embedded-mongo.version}</version>
        </dependency>
    

    MongoConfig

    @Configuration
    @EnableAutoConfiguration(exclude = { EmbeddedMongoAutoConfiguration.class })
    public class MongoConfig{
    }
    

    【讨论】:

    • 你能解释一下“@EnableAutoConfiguration(exclude = { EmbeddedMongoAutoConfiguration.class })”注解实际上在做什么吗?
    • 原因很可能是 de.flapdoodle.embed.mongo 依赖项未标记为测试范围。不要在生产应用程序设置中选择并运行嵌入式 mongo,需要排除。
    【解决方案7】:

    从 3.2.6 版开始,您可以在内存中运行 MongoDB。来自site

    从 MongoDB Enterprise 版本 3.2.6 开始,内存存储 引擎是 64 位版本中通用可用性 (GA) 的一部分。 除了一些元数据和诊断数据,内存存储 引擎不维护任何磁盘数据,包括配置 数据、索引、用户凭据等

    【讨论】:

      【解决方案8】:

      这是accepted answer from @rozky 的更新(2019 年)版本(Mongo 和 Embedded MongoDB 库中进行了很多更改)。

      package com.example.mongo;
      
      import com.mongodb.BasicDBObject;
      import com.mongodb.MongoClient;
      import com.mongodb.client.MongoCollection;
      import com.mongodb.client.MongoDatabase;
      import de.flapdoodle.embed.mongo.MongodExecutable;
      import de.flapdoodle.embed.mongo.MongodProcess;
      import de.flapdoodle.embed.mongo.MongodStarter;
      import de.flapdoodle.embed.mongo.config.IMongodConfig;
      import de.flapdoodle.embed.mongo.config.MongodConfigBuilder;
      import de.flapdoodle.embed.mongo.config.Net;
      import de.flapdoodle.embed.mongo.distribution.Version;
      import de.flapdoodle.embed.process.runtime.Network;
      import java.util.Date;
      import org.junit.After;
      import static org.junit.Assert.*;
      import org.junit.Before;
      import org.junit.Test;
      
      public class EmbeddedMongoTest
      {
          private static final String DATABASE_NAME = "embedded";
      
          private MongodExecutable mongodExe;
          private MongodProcess mongod;
          private MongoClient mongo;
      
          @Before
          public void beforeEach() throws Exception {
              MongodStarter starter = MongodStarter.getDefaultInstance();
              String bindIp = "localhost";
              int port = 12345;
              IMongodConfig mongodConfig = new MongodConfigBuilder()
              .version(Version.Main.PRODUCTION)
              .net(new Net(bindIp, port, Network.localhostIsIPv6()))
              .build();
              this.mongodExe = starter.prepare(mongodConfig);
              this.mongod = mongodExe.start();
              this.mongo = new MongoClient(bindIp, port);
          }
      
          @After
          public void afterEach() throws Exception {
              if (this.mongod != null) {
                  this.mongod.stop();
                  this.mongodExe.stop();
              }
          }
      
          @Test
          public void shouldCreateNewObjectInEmbeddedMongoDb() {
              // given
              MongoDatabase db = mongo.getDatabase(DATABASE_NAME);
              db.createCollection("testCollection");
              MongoCollection<BasicDBObject> col = db.getCollection("testCollection", BasicDBObject.class);
      
              // when
              col.insertOne(new BasicDBObject("testDoc", new Date()));
      
              // then
              assertEquals(1L, col.countDocuments());
          }
      
      }
      

      【讨论】:

      • 对于每个测试重复启动和停止嵌入式 mongo 会导致大多数测试失败。最好在所有测试之前启动并在所有测试都执行后关闭
      • 您需要在上面的更改中包含@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
      • @DBS 您也可以使用随机端口,这样您仍然可以在新的嵌入式 mongo 实例上同时运行测试。请参阅文档here
      • 是的,这就像一个魅力。从 baeldung 来到这里 - 这个例子一直在抛出 原因:java.lang.IllegalArgumentException:数据库名称不能为空!虽然我确实在 (MongoClients.create(String.format(CONNECTION_STRING, ip, port)), "cipresale");
      【解决方案9】:

      不仅用于单元测试,还解释了如何将inmemory mongodb与rest api一起使用。

      maven 依赖:

              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-data-mongodb</artifactId>
              </dependency>
      
              <dependency>
                  <groupId>de.flapdoodle.embed</groupId>
                  <artifactId>de.flapdoodle.embed.mongo</artifactId>
              </dependency>
      

      ================================================ ===============================

      application.properties

      server.port = 8080
      spring.data.mongodb.database=user_db
      spring.data.mongodb.port=27017
      spring.data.mongodb.host=localhost
      

      ================================================ ===============================

      UserRepository.java

      公共接口 UserRepository 扩展 MongoRepository{

      }

      供参考,所有java代码使用以下链接:(逐步解释)

      https://www.youtube.com/watch?v=2Tq2Q7EzhSA&t=7s

      【讨论】:

        【解决方案10】:

        使用storageEngine='ephemeralForTest' 执行mongod 时性能更好

        new MongodConfigBuilder()
            .version(Version.Main.PRODUCTION)
            .cmdOptions(new MongoCmdOptionsBuilder()
                 .useStorageEngine("ephemeralForTest")
                 .build())
            .net(new Net("localhost", port, Network.localhostIsIPv6()))
            .build()
        

        【讨论】:

          【解决方案11】:

          要运行嵌入式 mongodb 进行集成测试,需要以下 maven 依赖项:

                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-data-mongodb</artifactId>
                      <version>2.5.2</version>
                  </dependency>
          
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-autoconfigure</artifactId>
                      <version>2.5.2</version>
                  </dependency>
          
                  <dependency>
                      <groupId>de.flapdoodle.embed</groupId>
                      <artifactId>de.flapdoodle.embed.mongo</artifactId>
                      <version>3.0.0</version>
                      <scope>test</scope>
                  </dependency>
          

          尝试使用为EmbeddedMongoAutoConfiguration 截取的以下代码:

          import org.springframework.boot.SpringApplication;
          import org.springframework.boot.autoconfigure.SpringBootApplication;
          import org.springframework.boot.autoconfigure.mongo.MongoProperties;
          import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration;
          import org.springframework.context.ApplicationContext;
          import org.springframework.context.annotation.Bean;
          
          @SpringBootApplication
          public class EmbeddedMongoApplication {
          
              public static void main(String[] args) {
                   System.setProperty("os.arch", "x86_64");
                   SpringApplication.run(EmbeddedMongoApplication.class, args);
              }
              
              @Bean
              public EmbeddedMongoAutoConfiguration embeddedMongoAutoConfiguration(MongoProperties mongoProperties) {
                  return new EmbeddedMongoAutoConfiguration(mongoProperties);
              }
          }
          

          注意:

          嵌入的 mongodb 将被下载到下面的路径。所以要考虑到路径有适当的权限。

          Linux : $HOME/.embedmongo/linux/mongodb-linux-x86_64-3.2.2.tgz
          Windows : C:\Users\<username>\.embedmongo\win32\mongodb-win32-x86_64-3.x.x.zip
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-08-26
            • 1970-01-01
            • 2020-06-09
            • 2013-06-29
            • 2011-02-02
            • 2014-12-12
            • 2020-05-12
            • 2015-09-05
            相关资源
            最近更新 更多