首页 > 解决方案 > pytest 模拟 MongoReplicaSetClient 对象的 .count() 和 .find() 函数

问题描述

我有一个名为 commons.py 的脚本,其中包含一些最常用的功能。

在我的主脚本中,我正在创建一个 mongo 连接对象:

db_ob = commons.db_connection()

db_connection 返回一个 MongoReplicaSetClient 连接对象。

如何为我的以下功能编写测试用例?

def check_entries():
    try:
        db_ob = commons.db_connection()
    except:
        print('Unable to connect to db!!')
        return False
    db_name = 'my_db_name'
    collection_name = 'my_collection_name'
    db_data = db_ob[db_name][collection_name].find()
    if db_data.count() == 0:
        print('No entries found in the collection!')
        return False
    return True

我可以模拟我的 db_connection 函数,但在模拟 .count() 和 .find() 函数时遇到问题。

标签: pythontestingmockingpymongopytest

解决方案


当结果集为空时模拟光标以测试案例的示例:

from unittest.mock import patch, MagicMock
from pymongo.mongo_replica_set_client import MongoReplicaSetClient
from pymongo.cursor import Cursor
import testee


def test_empty_result_set():
    db_data_mock = MagicMock(spec=Cursor)()  # 1
    db_data_mock.count.return_value = 0  # 2
    db_conn_mock = MagicMock(spec=MongoReplicaSetClient)()  # 3
    db_conn_mock.__getitem__().__getitem__().find.return_value = db_data_mock
    with patch('commons.db_connection', return_value=db_conn_mock):  # 4
        assert not testee.check_entries()  # 5

细节:

  1. MagicMock(spec=Cursor)返回一个模仿类的pymongo.cursor.Cursor类。db_data_mock是这个模拟类的一个实例。
  2. db_data_mock.count.return_value = 0模拟该count方法,因此它始终返回零。

  3. 接下来的两行:为pymongo.mongo_replica_set_client.MongoReplicaSetClient(与 1. 中的方法相同)创建一个模拟实例并将光标模拟实例连接到它,以便该find()方法始终返回db_data_mock我们之前创建的实例。

  4. 最后,用commons.db_connection返回我们的MongoReplicaSetClient模拟对象的模拟函数替换该函数。
  5. 一切准备就绪;做实际测试。

unittest更新:评论中要求进行不使用的测试

如果出于某种奇怪的“纯粹性”原因,您不想接触unittest代码,则必须为此找到替代库,或者自己编写模拟。pytest不提供开箱即用的模拟功能。上面没有的示例unittest可能如下所示:

from collections import defaultdict
import testee


class CursorMock:

    def count(self):
        return 0


class ConnectionMock:

    def find(self):
        return CursorMock()


class MongoReplicaSetClientMock:

    def __getitem__(self, name):
        return defaultdict(ConnectionMock)


def db_connection_mock(*args, **kwargs):
    return MongoReplicaSetClientMock()


def test_empty_result_set(monkeypatch):
    monkeypatch.setattr(commons, 'db_connection', db_connection_mock)
    assert not testee.check_entries()

代替夹具unittest.mock.patch被使用。monkeypatch

请注意,尽管有一些pytest提供模拟功能的插件(例如,pytest-mock),但我所知道的大多数只是方便的包装器,unittest.mock并且仍然在引擎盖下使用它。


推荐阅读