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

Python 如何模拟构造函数中实例化的对象?,python,unit-testing,pytest,Python,Unit Testing,Pytest,我正在用Pytest编写单元测试。我想对一个类进行单元测试,该类在其\uuuu init\uuu方法上有一个连接到数据库的对象: 数据_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):

我正在用Pytest编写单元测试。我想对一个类进行单元测试,该类在其
\uuuu init\uuu
方法上有一个连接到数据库的对象:

数据_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
class DataBaseWrapper:
  def __init__(self):
    # Init process of the wrapper
    pass

  def update_data(self):
    # Connect to the database and perform some operations
    pass
数据库包装器.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
class DataBaseWrapper:
  def __init__(self):
    # Init process of the wrapper
    pass

  def update_data(self):
    # Connect to the database and perform some operations
    pass
我尝试在
DataModel
DataBaseWrapper
对象上使用
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
但是,我得到以下错误:

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'
monkeypatch=
@pytest.fixture
def数据_型号(monkeypatch):
>setattr(数据模型'db',MockDataBaseWrapper)
E AttributeError:没有属性“db”
我在类似问题的答案中读到,我可以尝试编写我的
数据库包装器的一个子类
,并在
数据模型
类上更改它,但我处于相同的情况,因为我无法
monkeypatch
方法的属性。但是,如果它不在
\uuuu init\uuu
方法中,我可以


我如何为这些类的组合编写测试?关于如何重写这些类或不同模式的建议也很受欢迎。

问题是您的
MockDataBaseWrapper
DataModel
中使用的
DataBaseWrapper
完全无关

我的建议是去掉你的
MockDataBaseWrapper
,然后:

  • 如果要保持当前结构,可以使用
    patch
    模拟实际导入到data\u model.py中的
    DataBaseWrapper
补丁
上下文管理器将用模拟实例替换导入data_model.py中的
DataBaseWrapper
类,并允许您与该模拟交互,这允许我在此验证它是否已实例化

请注意,在导入类的模块中(而不是在定义它的模型中,即我们修补
您的\u包.data\u模型.DataBaseWrapper
而不是
您的\u包.data\u base\u wrapper.DataBaseWrapper
)对类进行修补非常重要

  • 如果您不介意更改您的类,那么通常的模式是将
    db
    参数注入到
    DataModel
    的构造函数中。嘲笑它就成了小菜一碟

问题是您的
MockDataBaseWrapper
DataModel
中使用的
DataBaseWrapper
完全无关

我的建议是去掉你的
MockDataBaseWrapper
,然后:

  • 如果要保持当前结构,可以使用
    patch
    模拟实际导入到data\u model.py中的
    DataBaseWrapper
补丁
上下文管理器将用模拟实例替换导入data_model.py中的
DataBaseWrapper
类,并允许您与该模拟交互,这允许我在此验证它是否已实例化

请注意,在导入类的模块中(而不是在定义它的模型中,即我们修补
您的\u包.data\u模型.DataBaseWrapper
而不是
您的\u包.data\u base\u wrapper.DataBaseWrapper
)对类进行修补非常重要

  • 如果您不介意更改您的类,那么通常的模式是将
    db
    参数注入到
    DataModel
    的构造函数中。嘲笑它就成了小菜一碟

使您的代码更易于测试:不要在
\uuuuuu init\uuuuuuu
内部硬编码
DataBaseWrapper
,而是使用
DataModel.\uuuuuuuuuuu
作为参数,将
self.db
使用的值作为参数,或者在夹具中调用一个类来设置
self.db
(例如def\uuu init(self,factory):self.db=factory(),首先创建一个
DataModel
的实例,然后修补实例上的
db
属性。使您的代码更易于测试:不要在
\uuuuu init\uuuuu
内硬编码
数据库包装器,而是使用
数据模型。\uuuu init\uuuuu
将用于
self.db
的值作为参数,或者可以调用一个类来设置fixture中的
self.db
(例如,def_uuinit_uu(self,factory):self.db=factory()`),首先创建一个
DataModel
的实例,然后修补实例上的
db
属性。尽管我最终采用了第二种方法(更改了我的类定义)我对理解第一个很感兴趣。我猜
MockedDB
是在测试文件本身中定义的,对吗?(为了完整性,我认为这对未来的读者来说是一个很好的补充)我已经澄清了答案,让我知道这样是否更容易理解尽管我最终选择了第二种方法(改变了我的类定义),但我对理解第一种方法很感兴趣。我猜
MockedDB
是在测试文件本身中定义的,对吗?(为了完整性,我认为这对未来的读者来说是一个很好的补充)我已经澄清了答案,让我知道这样是否更容易理解
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