运行集成测试时嵌入 MongoDB
Posted
技术标签:
【中文标题】运行集成测试时嵌入 MongoDB【英文标题】:Embedded MongoDB when running integration tests 【发布时间】:2011-09-20 04:51:00 【问题描述】:我的问题是this one 的变体。
由于我的 Java Web 应用程序项目需要大量读取过滤器/查询以及与 GridFS 等工具的接口,因此我正在努力想出一种以上述解决方案建议的方式使用 MongoDB 的明智方法。
因此,我正在考虑在集成测试的同时运行一个嵌入式 MongoDB 实例。我希望它自动启动(对于每个测试或整个套件),刷新数据库对于每个测试,然后关闭 最后。这些测试可能会在开发机器和 CI 服务器上运行,所以我的解决方案也需要便携。
任何对 MongoDB 有更多了解的人都可以帮助我了解这种方法的可行性,和/或建议任何可以帮助我入门的阅读材料吗?
我也愿意接受人们可能提出的关于如何解决这个问题的其他建议...
【问题讨论】:
如果你使用的是maven,你可以使用我们的mvnrepository.com/artifact/com.wenzani/mongodb-maven-plugin 你也可以查看这个项目,它在 JVM 内存中模拟了一个 MongoDB。 github.com/thiloplanz/jmockmongo 但它仍在开发中。 不 [仅用于] 单元测试,但如果您使用 Linux 时希望将 MongoDB(甚至是集群)作为内存部署运行,请阅读这篇博文。 edgystuff.tumblr.com/post/49304254688 不过,如果能像 RavenDB 一样开箱即用,那就太好了。 与这里提到的 embedmongo-maven-plugin 类似,也有一个Gradle Mongo Plugin 可用。与 Maven 插件一样,它也包装了 flapdoodle EmbeddedMongoDb api,并允许您从 Gradle 构建中运行 Mongo 的托管实例。 在此处查看此代码示例:github.com/familysyan/embedded-mongo-integ。无需安装,无需依赖。它只是一个独立于平台的 ant 脚本,可以为您下载和设置。它还会在您的测试后清理所有内容。 【参考方案1】:我找到了Embedded MongoDB 库,它看起来很有前途,可以满足您的要求。
当前支持 MongoDB 版本:1.6.5
到 3.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.mongodb
Java 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
以供将来构建。
【讨论】:
这里是 Leiningen 的 Clojure 版本:github.com/joelittlejohn/lein-embongo【参考方案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
【讨论】:
以上是关于运行集成测试时嵌入 MongoDB的主要内容,如果未能解决你的问题,请参考以下文章
在使用 Springboot 运行集成测试时启动嵌入式 gRPC 服务器
多语言堆栈的集成测试(Java/MongoDB/RabbitMQ...)