简体   繁体   English

如何模拟在构造函数中实例化的对象?

[英]How can I mock an object instantiated in the constructor?

I'm writing unit-tests with Pytest. 我正在用Pytest编写单元测试。 I want to unit-test a class that has on its __init__ method an object that connects to a database: 我想对在其__init__方法上具有连接到数据库的对象的类进行单元测试:

data_model.py data_model.py

from my_pkg.data_base_wrapper import DataBaseWrapper

class DataModel:
  def __init__(self):
    self.db = DataBaseWrapper()
    self.db.update_data()

  def foo(self):
    data = self.db.get_some_data()
    # make some processing and return a result

data_base_wrapper.py data_base_wrapper.py

class DataBaseWrapper:
  def __init__(self):
    # Init process of the wrapper
    pass

  def update_data(self):
    # Connect to the database and perform some operations
    pass

I've tried using monkeypatch on the DataBaseWrapper object of DataModel : 我试过在DataModelDataBaseWrapper对象上使用monkeypatch

from my_pkg.data_model import DataModel

class MockDataBaseWrapper:
  @staticmethod
  def update_cache():
      pass

  @staticmethod
  def get_table(table):
    # Return some data for testing
    pass

@pytest.fixture
def data_model(monkeypatch):
  monkeypatch.setattr(DataModel, 'db', MockDataBaseWrapper)
  data_model = DataModel()

  return data_model

However, I get the following error: 但是,出现以下错误:

monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f10221669e8>

    @pytest.fixture
    def data_model(monkeypatch):
>       monkeypatch.setattr(DataModel, 'db', MockDataBaseWrapper)
E       AttributeError: <class 'dashboard.datamodel.DataModel'> has no attribute 'db'

I've read in the answers of similar questions, that I could try writing a sub-class of my DataBaseWrapper and change it on the DataModel class, but I'm in the same situation, as I cannot monkeypatch the attribute of the __init__ method. 我已经读过类似问题的答案,我可以尝试编写DataBaseWrapper的子类并在DataModel类上进行更改,但我处在相同的情况下,因为我无法对__init__方法的属性进行monkeypatch I can, though, if it is not in the __init__ method. 但是,如果不在__init__方法中,则可以。

How can I write tests for this composition of classes? 我该如何为此类课程编写测试? Suggestions on how to rewrite these classes or a different patter are also welcome. 也欢迎提出有关如何重写这些类或不同模式的建议。

The problem is that your MockDataBaseWrapper is totally unrelated to the DataBaseWrapper used in DataModel . 问题是您的MockDataBaseWrapperDataModel使用的DataBaseWrapper完全无关。

My proposal is to get rid of your MockDataBaseWrapper and: 我的建议是摆脱MockDataBaseWrapper并:

  • If you want to keep your current structure, you can use patch to mock the DataBaseWrapper that is actually imported in data_model.py. 如果要保留当前结构,则可以使用patch来模拟实际导入到data_model.py中的DataBaseWrapper
from mock import patch
from my_pkg.data_model import DataModel

def test_data_model():
    with patch("my_pkg.data_model.DataBaseWrapper") as MockedDB:
        mocked_db = MockedDB()
        data_model = DataModel()
        assert data_model.db is mocked_db

The patch context manager will replace the DataBaseWrapper class that gets imported in your data_model.py with a Mock instance and let you interact with that mock, which allows me here to verify that it got instantiated. patch上下文管理器将使用Mock实例替换在data_model.py中导入的DataBaseWrapper类,并让您与该模拟进行交互,这使我在此处可以验证它是否已实例化。

Note that it is very important to patch the class in the module where it is imported (and not in the model where it is defined, ie we patch your_package.data_model.DataBaseWrapper and not your_package.data_base_wrapper.DataBaseWrapper ) 请注意,在导入该模块的模块中修补该类非常重要(而不是在定义该模块的模型中进行修补,即我们修补your_package.data_model.DataBaseWrapper而不是your_package.data_base_wrapper.DataBaseWrapper

  • If you don't mind changing your class, then the usual pattern is to inject the db parameter into the constructor of DataModel . 如果您不介意更改类,那么通常的模式是将db参数注入DataModel的构造函数中。 Mocking it then becomes a piece of cake. 然后模拟它就成了小菜一碟。
class DataModel:
    def __init__(self, db):
        self.db = db
        self.db.update_data()

from mock import patch, Mock
from my_pkg.data_model import DataModel

def test_data_model():
    mocked_db = Mock()
    data_model = DataModel(mocked_db)
    assert data_model.db is mocked_db

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM