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

时间:2019-09-09 18:42:37

标签: python unit-testing pytest

我正在用Pytest编写单元测试。我想对在其__init__方法上具有连接到数据库的对象的类进行单元测试:

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

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

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

我尝试在monkeypatch的{​​{1}}对象上使用DataBaseWrapper

DataModel

但是,出现以下错误:

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

我已经读过类似问题的答案,可以尝试编写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' 的子类并在DataBaseWrapper类上进行更改,但是我处在相同的情况下,因为我无法DataModel方法的属性。但是,如果不在monkeypatch方法中,我可以。

如何为此类课程编写测试?也欢迎提出有关如何重写这些类或其他模式的建议。

1 个答案:

答案 0 :(得分:1)

问题在于您的MockDataBaseWrapperDataBaseWrapper中使用的DataModel完全无关。

我的建议是摆脱您的MockDataBaseWrapper和:

  • 如果要保留当前结构,可以使用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

patch上下文管理器将使用Mock实例替换在data_model.py中导入的DataBaseWrapper类,并让您与该模拟进行交互,这使我在此处可以验证它是否已实例化

  

请注意,在导入类的模块中修补该类非常重要(而不是在定义该类的模型中进行修补,即,我们修补your_package.data_model.DataBaseWrapper而不是your_package.data_base_wrapper.DataBaseWrapper

  • 如果您不介意更改类,则通常的模式是将db参数注入DataModel的构造函数中。然后嘲笑它就成了小菜一碟。
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