使用MongoDB进行单元测试

wul*_*pro 61 java junit unit-testing mongodb morphia

我选择的数据库是MongoDB.我正在编写一个数据层API来从客户端应用程序中抽象实现细节 - 也就是说,我实际上提供了一个单独的公共接口(一个充当IDL的对象).

我正在以TDD的方式测试我的逻辑.在每个单元测试之前,@Before调用一个方法来创建数据库单例,之后,当测试完成时,@After调用一个方法来删除数据库.这有助于提高单元测试之间的独立性.

几乎所有的单元测试,即执行上下文查询,都需要事先发生某种插入逻辑.我的公共接口提供了一个插入方法 - 然而,使用此方法作为每个单元测试的前驱逻辑似乎是不正确的.

我真的需要某种模拟机制,但是,我对模拟框架没有多少经验,而且Google似乎没有返回任何可能与MongoDB一起使用的模拟框架.

其他人在这些情况下做了什么?也就是说,人们如何单元测试与数据库交互的代码?

此外,我的公共接口连接到外部配置文件中定义的数据库 - 使用此连接进行单元测试似乎不正确 - 再次,这种情况会受益于某种类型的模拟?

sbr*_*ges 62

技术上与数据库(nosql或其他)通信的测试不是单元测试,因为测试正在测试与外部系统的交互,而不仅仅是测试隔离的代码单元.然而,与数据库通信的测试通常非常有用,并且通常足够快以与其他单元测试一起运行.

通常我有一个Service接口(例如UserService),它封装了处理数据库的所有逻辑.依赖于UserService的代码可以使用模拟版本的UserService并且易于测试.

在测试与Mongo(例如MongoUserService)对话的服务的实现时,最简单的方法是编写一些java代码来启动/停止本地机器上的mongo进程,让你的MongoUserService连接到那个,看看这个问题是否有些问题.笔记.

您可以尝试在测试MongoUserService时模拟数据库的功能,但通常这很容易出错,并且不会测试您真正想要测试的内容,即与真实数据库的交互.因此,在为MongoUserService编写测试时,您需要为每个测试设置数据库状态.查看DbUnit以获取使用数据库执行此操作的框架示例.

  • 希望有人为MongoDB发明类似DbUnit的框架-MongoUnit-现在... (3认同)

rit*_*rit 30

正如sbridges在这篇文章中所写,不要有专门的服务(有时也称为存储库或DAO)从逻辑中抽象出数据访问是一个坏主意.然后你可以通过提供DAO的模拟来测试逻辑.

我做的另一种方法是创建一个Mongo对象的模拟(例如PowerMockito),然后返回适当的结果.这是因为您不必测试数据库是否在单元测试中工作,但是您应该测试是否将正确的查询发送到数据库.

Mongo mongo = PowerMockito.mock(Mongo.class);
DB db = PowerMockito.mock(DB.class);
DBCollection dbCollection = PowerMockito.mock(DBCollection.class);

PowerMockito.when(mongo.getDB("foo")).thenReturn(db);
PowerMockito.when(db.getCollection("bar")).thenReturn(dbCollection);

MyService svc = new MyService(mongo); // Use some kind of dependency injection
svc.getObjectById(1);

PowerMockito.verify(dbCollection).findOne(new BasicDBObject("_id", 1));
Run Code Online (Sandbox Code Playgroud)

这也是一种选择.当然,模拟的创建和相应对象的返回仅作为上面的例子编码.


Ben*_*gel 17

我在Java中编写了一个MongoDB伪实现:mongo-java-server

默认值是内存后端,可以在单元和集成测试中轻松使用.

MongoServer server = new MongoServer(new MemoryBackend());
// bind on a random local port
InetSocketAddress serverAddress = server.bind();

MongoClient client = new MongoClient(new ServerAddress(serverAddress));

DBCollection coll = client.getDB("testdb").getCollection("testcoll");
// creates the database and collection in memory and inserts the object
coll.insert(new BasicDBObject("key", "value"));

assertEquals(1, collection.count());
assertEquals("value", collection.findOne().get("key"));

client.close();
server.shutdownNow();
Run Code Online (Sandbox Code Playgroud)


Eug*_*kin 8

今天,我认为最佳实践是在Python上使用testcontainers库(Java)或testcontainers-python端口。它允许将Docker映像与单元测试一起使用。要在Java代码中运行容器,只需实例化GenericContainer对象(例如):

GenericContainer mongo = new GenericContainer("mongo:latest")
    .withExposedPorts(27017);

MongoClient mongoClient = new MongoClient(mongo.getContainerIpAddress(), mongo.getMappedPort(27017));
MongoDatabase database = mongoClient.getDatabase("test");
MongoCollection<Document> collection = database.getCollection("testCollection");

Document doc = new Document("name", "foo")
        .append("value", 1);
collection.insertOne(doc);

Document doc2 = collection.find(new Document("name", "foo")).first();
assertEquals("A record can be inserted into and retrieved from MongoDB", 1, doc2.get("value"));
Run Code Online (Sandbox Code Playgroud)

或在Python上(例如):

mongo = GenericContainer('mongo:latest')
mongo.with_bind_ports(27017, 27017)

with mongo_container:
    def connect():
        return MongoClient("mongodb://{}:{}".format(mongo.get_container_host_ip(),
                                                    mongo.get_exposed_port(27017)))

    db = wait_for(connect).primer
    result = db.restaurants.insert_one(
        # JSON as dict object
    )

    cursor = db.restaurants.find({"field": "value"})
    for document in cursor:
        print(document)
Run Code Online (Sandbox Code Playgroud)

  • 我认为这是最好的方法。有了docker,我们就不再需要任何mock server了,自动测试就可以了。 (2认同)