Python 如何模拟构造函数中实例化的对象?
我正在用Pytest编写单元测试。我想对一个类进行单元测试,该类在其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):
\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
,然后:
- 如果要保持当前结构,可以使用
模拟实际导入到data\u model.py中的patch
李>DataBaseWrapper
补丁
上下文管理器将用模拟实例替换导入data_model.py中的DataBaseWrapper
类,并允许您与该模拟交互,这允许我在此验证它是否已实例化
请注意,在导入类的模块中(而不是在定义它的模型中,即我们修补您的\u包.data\u模型.DataBaseWrapper
而不是您的\u包.data\u base\u wrapper.DataBaseWrapper
)对类进行修补非常重要
- 如果您不介意更改您的类,那么通常的模式是将
参数注入到db
的构造函数中。嘲笑它就成了小菜一碟DataModel
问题是您的
MockDataBaseWrapper
与DataModel
中使用的DataBaseWrapper
完全无关
我的建议是去掉你的MockDataBaseWrapper
,然后:
- 如果要保持当前结构,可以使用
模拟实际导入到data\u model.py中的patch
李>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