【问题标题】:How to mock mongodb when it is called from another function?从另一个函数调用mongodb时如何模拟mongodb?
【发布时间】:2018-12-17 13:27:37
【问题描述】:

我在模拟 mongodb 时需要帮助。我正在使用mongomock 来模拟 mongodb。

我的项目结构是:

-- my_mongo.py
-- 代码.py
-- my_test.py

my_mongo.py 有:

from pymongo import MongoClient

 def get_db():
   client = MongoClient(os.environ['MONGODB_URI'])
   db = client['my_db']
   return db

 def insert(id, data):
     return get_db().results.insert_one(
          {
          "id": id,
          "data":df.to_json(),
          }).id

code.py

import my_mongo

def action():
    #do somethings
    my_mongo.insert(id, data)

my_test.py

import mongomock
import my_mongo
from unittest import mock


with patch.object(my_mongo.get_db().client, "client",  mongomock.MongoClient()):
    import code

def test_action_1():
    my_mongo.insert = mock.Mock(return_value=1)
    code.action()   

def test_action_2():
     with patch.object(my_mongo.get_db(), "get_db", mongomock.MongoClient().db):
     code.action()

这两个测试都会抛出 pymongo.errors.ServerSelectionTimeoutError。所以,它仍然进入 my_mongo.py 中的 insert_one() 方法。 我希望在 test_action_1 my_mongo.insert 中返回 1,但它没有。

我错过了什么?

【问题讨论】:

    标签: python mongodb unit-testing mocking mongomock


    【解决方案1】:

    我不完全确定mongomock 的用途,但它看起来像是用于模拟整个 mongo 数据库而不是实际使用 python 模拟。我将在不包括 mongomock 的情况下回答,因为我认为您并不真正需要它,因此您可以物有所值。

    有几个问题:

    1. 调用patch.object 将在你给它的任何对象上修补给定的方法。如果你在测试中调用get_db,那么code.action 调用get_db,那是两个不同的对象。也许这行得通?但我很怀疑,所以我只是改变了它。

    2. 不要使用 code 作为您的模块名称。这已经是python包含的一个模块了。

    3. code.action 缺少 args 和 return 语句。

    您还会注意到,我更改了模拟的方式和内容,以说明完成模拟的不同方式。测试 1 使用函数装饰器模拟 insert 调用。测试 2 使用 contextmanager 模拟 get_db 调用。任何一个都是正确的,只是表明你有选择。

    这是成品:


    my_mongo.py:

    from pymongo import MongoClient
    
    def get_db():
        client = MongoClient(os.environ['MONGODB_URI'])
        db = client['my_db']
        return db
    
    def insert(id, data):
        return get_db().results.insert_one({"id": id, "data":data.to_json()}).id  # df was undefined, updated to data
    

    my_code.py:

    import my_mongo
    
    # I added id and data args. They were undefined
    def action(id, data):
        return my_mongo.insert(id, data)  # I added a return here
    

    my_test.py

    from unittest import mock
    
    import my_code
    
    # I removed the contextmanager import. Nothing is being evaluated here that would
    # need to be patched, so I'm pretty certain it has no effect
    
    @mock.patch('my_mongo.insert')
    def test_action_1(mock_insert):
        expected_id = 1
        mock_insert.return_value = expected_id
        ret = my_code.action(expected_id, mock.Mock())
        assert ret == expected_id
    
    def test_action_2():
        with mock.patch('my_mongo.get_db') as mock_get_db:
            expected_id = 'some id'
            mock_db = mock.Mock()
            mock_db.results.insert_one.return_value.id = expected_id
            mock_get_db.return_value = mock_db
            ret = my_code.action(expected_id, mock.Mock())
        assert ret == expected_id
    

    【讨论】:

      【解决方案2】:

      修补 mongodb 的那行代码是错误的。您应该使用patch.object("my_mongo.get_db", return_value=mongomock.MongoClient()['my_db']),而不是使用patch.object(my_mongo.get_db(), "get_db", mongomock.MongoClient().db)


      以下是您的示例的完整可运行代码:

      my_test.py

      import mongomock
      from unittest.mock import patch
      
      import my_code
      import my_mongo
      
      
      def test_action_2():
          mocked_mongo = mongomock.MongoClient()
          with patch("my_mongo.get_db", return_value=mongomock.MongoClient()['my_db']):
              my_code.action()
              assert mocked_mongo.my_db.results.count_documents({'id': 'some_id'}) == 1
      

      my_mongo.py

      from pymongo import MongoClient
      
      def get_db():
          client = MongoClient(os.environ['MONGODB_URI'])
          db = client['my_db']
          return db
      
      def insert(id, data):
          return get_db().results.insert_one(
              {
                  "id": id,
                  "data": data,
              })
      

      my_code.py

      import my_mongo
      
      def action():
          #do somethings
          return my_mongo.insert('some_id', '{"a": 3}')
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-12-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多