python - pytest/unittest : mock. 来自模块的补丁功能?

标签 python mocking pytest python-unittest

给定这样的文件夹结构:

dags/
  **/
    code.py
tests/
  dags/
    **/
      test_code.py
  conftest.py
其中 dags 作为 src 文件的根,“dags/a/b/c.py”作为“a.b.c”导入。
我想在 code.py 中测试以下函数:
from dag_common.connections import get_conn
from utils.database import dbtypes

def select_records(
    conn_id: str,
    sql: str,
    bindings,
):
    conn: dbtypes.Connection = get_conn(conn_id)
    with conn.cursor() as cursor:
        cursor.execute(
            sql, bindings
        )
        records = cursor.fetchall()
    return records
但我面临的问题是我找不到修补 get_conn 的方法。来自 dag_common.connections .我尝试了以下操作:
(1) 全局在 conftest.py
import os
import sys

# adds dags to sys.path for tests/*.py files to be able to import them
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "dags"))

{{fixtures}}
我已经测试了 {{fixtures}} 的以下替代品:
(1.a) - 默认
@pytest.fixture(autouse=True, scope="function")
def mock_get_conn():
    with mock.patch("dag_common.connections.get_conn") as mock_getter:
        yield mock_getter
(1.b) - 用 dags 前缀路径
@pytest.fixture(autouse=True, scope="function")
def mock_get_conn():
    with mock.patch("dags.dag_common.connections.get_conn") as mock_getter:
        yield mock_getter
(1.c) - 1.a, 作用域=" session "
(1.d) - 1.b, 作用域=" session "
(1.e) - 修补模块本身的对象
@pytest.fixture(autouse=True, scope="function")
def mock_get_conn():
    import dags.dag_common.connections
    mock_getter = mock.MagicMock()
    with mock.patch.object(dags.dag_common.connections, 'get_conn', mock_getter):
        yield mock_getter
(1.f) - 1.a,但使用 pytest-mock fixture
@pytest.fixture(autouse=True, scope="function")
def mock_get_conn(mocker):
    with mocker.patch("dag_common.connections.get_conn") as mock_getter:
        yield mock_getter
(1.g) - 1.b,但使用 pytest-mock fixture
(1.h) - 1.a,但使用 pytest 的monkeypatch
@pytest.fixture(autouse=True, scope="function")
def mock_get_conn(mocker, monkeypatch):
    import dags.dag_common.connections
    mock_getter = mocker.MagicMock()
    monkeypatch.setattr(dags.dag_common.connections, 'get_conn', mock_getter)
    yield mock_getter
(2)在测试中本地应用mock.patch/作为装饰器
(2.a) - 装饰器@mock.patch("dag_common.connections.get_conn")
    @mock.patch("dag_common.connections.get_conn")
    def test_executes_sql_with_default_bindings(mock_getter, mock_context):
        # arrange
        sql = "SELECT * FROM table"
        records = [RealDictRow(col1=1), RealDictRow(col1=2)]
        mock_conn = mock_getter.return_value
        mock_cursor = mock_conn.cursor.return_value
        mock_cursor.execute.return_value = records
        # act
        select_records(conn_id="orca", sql=sql, ) # ...
        # assert
        mock_cursor.execute.assert_called_once_with(
            sql, # ...
        )
(2.b) - (2.a) 但带有“dags”。前缀
(2.c) - 上下文管理器
    def test_executes_sql_with_default_bindings(mock_context):
        # arrange
        sql = "SELECT * FROM table"
        records = [RealDictRow(col1=1), RealDictRow(col1=2)]
        with mock.patch("dag_common.connections.get_conn") as mock_getter:
            mock_conn = mock_getter.return_value
            mock_cursor = mock_conn.cursor.return_value
            mock_cursor.execute.return_value = records
            # act
            select_records(conn_id="orca", sql=sql, ) # ...
            # assert
            mock_cursor.execute.assert_called_once_with(
                sql, # ...
            )
(2.d) - (2.c) 但带有“dags”。前缀

结论
但唉,无论我选择什么解决方案,待模拟的函数仍然会被调用。
我确保分别尝试每个解决方案,并在两次尝试之间终止/清除/重新启动我的 pytest-watch 过程。
我觉得这可能与我在 conftest.py 中干预 sys.path 有关,因为除此之外我觉得我已经用尽了所有的可能性。
知道我该如何解决这个问题吗?

最佳答案

是的。当我学习打补丁和 mock 时,我最初也与此作斗争,并且知道它是多么令人沮丧,因为您似乎一切都做对了,但它不起作用。我同情你!
这实际上是对进口东西的 mock 的工作方式,一旦你意识到这一点,它实际上是有道理的。
问题在于导入的工作方式是使导入的模块在导入的上下文中可用。
让我们假设您的 code.py模块位于“my_package”文件夹中。您的代码可用,然后作为 my_package.code .一旦你使用 from dag_common.connections import get_conncode模块 - 导入的 get_conn变得可用.... my_package.code.get_conn在这种情况下,您需要修补 my_package.code.get_conn不是您从中导入 get_conn 的原始包。
一旦你意识到这一点,打补丁就变得容易多了。

关于python - pytest/unittest : mock. 来自模块的补丁功能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69276878/

相关文章:

python - 使两个类互相引用

c# - 在 EntityFramework 4.1 中如何使用 Moles 从查询数据库中窃取 DbContext?

rest - 如何使用stubby4j stub 某些请求并为其他请求调用真实服务

python - mock.patch 在 pytest fixture 中不起作用?

python - pytest:如何定义强制命令行参数

python - Python 中列表的语法

python - 使用 Opencv python 从图像中删除蒙版圆圈

python - 在 PyCharm 中以退出代码 137 结束进程

.net - Moles 与其他隔离框架(例如 Moq、RhinoMocks 等)有何不同

python - pytest 失败,错误为 "ERROR: file or directory not found: and"