pytest模拟MongoReplicaSetClient对象的.count()和.find()函数

时间:2018-04-30 12:40:00

标签: python testing mocking pymongo pytest

我有一个名为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()函数时遇到了问题。

2 个答案:

答案 0 :(得分:1)

模拟光标以在结果集为空时测试大小写的示例:

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()方法始终返回{{1我们之前创建的实例。

  4. 最后,将db_data_mock函数替换为返回commons.db_connection模拟对象的模拟函数。
  5. 所有准备工作;做实际测试。
  6. 更新:在评论

    中请求了不使用MongoReplicaSetClient的测试

    如果,对于一些奇怪的"纯度"原因是,您不想触摸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() 使用了http://edgeguides.rubyonrails.org/action_controller_overview.html#strong-parameters

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

答案 1 :(得分:0)

我的解决方案:

由于db_connection函数正在返回一个对象,我使我的mock函数返回一个对象,并且内置了所需的函数。

我开始使用所需的函数创建一个类,并以所需的格式返回对象。

基于各种情况(成功/不成功连接,各种计数值等),可能有几个模拟函数。

def mock_db_connection_success(*args, **kwargs):
    """Function to mock successful db connection."""
    class ReturnClass(object):
        """."""
        @staticmethod
        def __init__(*args, **kwargs):
            """."""
            pass

        # @staticmethod
        def find(self, *args, **kwargs):
            """."""
            return self

        @staticmethod
        def count(*args, **kwargs):
            """."""
            return 1
    class_ob = ReturnClass()
    return {"my_db_name": {"my_collection_name": class_ob}}


@mock.patch('my_script.commons.db_connection', side_effect=mock_db_connection_success)
def test_function(*args, **kwargs):
    """Test Function."""
    # use the function here

我还使用python库找到了相同的实现:flexmock

使用该库,我们不需要为类编写完整的定义,并且可以使用最低限度的定义来完成。它为更快地编写各种案例提供了很多帮助。