使用 pytest-mock 模拟对象和对象方法
Using pytest-mock to mock objects and object methods
我正在尝试使用 pytest-mock
进行模拟。这个库本质上是 plugin/wrapper for mock
and patch
.
我的问题定义为:
我有一个使用 SQL Alchemy 的应用程序 (mymodule.py
)。基本上,有一个函数可以定义 SQL Alchemy 中的一些对象和 returns 包含这些对象的字典。
def some_function1():
# some code
from sqlalchemy import create_engine, MetaData, Table
engine = create_engine(f"mysql+pymysql://{username}:{password}@{host}:{port}")
meta = MetaData(engine)
my_table = Table(
'my_table',
meta,
autoload=True,
schema="some_schema"
)
db_tools = {"engine": engine, "table": my_table}
return db_tools
然后,第二个函数将该输出字典作为输入并使用它们:
def some_function2(db_tools, data):
sql_query = db_tools["table"].insert().values(data)
db_tools["engine"].execute(sql_query)
# some more code
所以现在我在写单元测试,不想真正和真实的数据库进行通信。所以我只需要模拟所有 sqlalchemy
相关的东西。到目前为止,我已经通过以下方式成功地模拟了 create_engine
、MetaData
和 Table
:
mocker.patch(
'my_module.create_engine',
return_value=True
)
mocker.patch(
'my_module.MetaData',
return_value=True
)
mocker.patch(
'my_module.Table',
return_value=True
)
这让我可以测试 some_function1
。但现在我需要测试 some_function2
,它使用方法或属性 .insert()
、.values
和 .execute()
。我该如何修补它?
模拟 some_function1
没有太多好处,因为它除了建立与数据库的连接外什么都不做。它不需要任何输入,它 returns 只是一个指向 table 和一个连接的字典。关于 some_function2
我们可以在 db_tools
参数中传入多个 MagicMock
并使用 configure_mock.
def test_some_function2(mocker):
mock_table = mocker.MagicMock()
mock_engine = mocker.MagicMock()
fake_query = "INSERT blah INTO foo;"
fake_data = [2, 3]
mock_table.configure_mock(
**{
"insert.return_value": mock_table,
"values.return_value": fake_query
}
)
db_tools = {
"table": mock_table,
"engine": mock_engine
}
some_function2(db_tools, fake_data)
mock_table.insert.assert_called_once()
mock_table.values.assert_called_once_with(fake_data)
mock_engine.execute.assert_called_once_with(fake_query)
当测试是运行它returns以下。
========================================================== test session starts ==========================================================
platform darwin -- Python 3.7.4, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: ***
plugins: mock-3.2.0
collected 1 item
test_foo.py . [100%]
=========================================================== 1 passed in 0.01s ===========================================================
我正在尝试使用 pytest-mock
进行模拟。这个库本质上是 plugin/wrapper for mock
and patch
.
我的问题定义为:
我有一个使用 SQL Alchemy 的应用程序 (mymodule.py
)。基本上,有一个函数可以定义 SQL Alchemy 中的一些对象和 returns 包含这些对象的字典。
def some_function1():
# some code
from sqlalchemy import create_engine, MetaData, Table
engine = create_engine(f"mysql+pymysql://{username}:{password}@{host}:{port}")
meta = MetaData(engine)
my_table = Table(
'my_table',
meta,
autoload=True,
schema="some_schema"
)
db_tools = {"engine": engine, "table": my_table}
return db_tools
然后,第二个函数将该输出字典作为输入并使用它们:
def some_function2(db_tools, data):
sql_query = db_tools["table"].insert().values(data)
db_tools["engine"].execute(sql_query)
# some more code
所以现在我在写单元测试,不想真正和真实的数据库进行通信。所以我只需要模拟所有 sqlalchemy
相关的东西。到目前为止,我已经通过以下方式成功地模拟了 create_engine
、MetaData
和 Table
:
mocker.patch(
'my_module.create_engine',
return_value=True
)
mocker.patch(
'my_module.MetaData',
return_value=True
)
mocker.patch(
'my_module.Table',
return_value=True
)
这让我可以测试 some_function1
。但现在我需要测试 some_function2
,它使用方法或属性 .insert()
、.values
和 .execute()
。我该如何修补它?
模拟 some_function1
没有太多好处,因为它除了建立与数据库的连接外什么都不做。它不需要任何输入,它 returns 只是一个指向 table 和一个连接的字典。关于 some_function2
我们可以在 db_tools
参数中传入多个 MagicMock
并使用 configure_mock.
def test_some_function2(mocker):
mock_table = mocker.MagicMock()
mock_engine = mocker.MagicMock()
fake_query = "INSERT blah INTO foo;"
fake_data = [2, 3]
mock_table.configure_mock(
**{
"insert.return_value": mock_table,
"values.return_value": fake_query
}
)
db_tools = {
"table": mock_table,
"engine": mock_engine
}
some_function2(db_tools, fake_data)
mock_table.insert.assert_called_once()
mock_table.values.assert_called_once_with(fake_data)
mock_engine.execute.assert_called_once_with(fake_query)
当测试是运行它returns以下。
========================================================== test session starts ==========================================================
platform darwin -- Python 3.7.4, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: ***
plugins: mock-3.2.0
collected 1 item
test_foo.py . [100%]
=========================================================== 1 passed in 0.01s ===========================================================