@Patch 装饰器与 pytest 夹具不兼容

Posted

技术标签:

【中文标题】@Patch 装饰器与 pytest 夹具不兼容【英文标题】:@Patch decorator is not compatible with pytest fixture 【发布时间】:2014-09-23 07:24:21 【问题描述】:

我在使用与 pytest 夹具集成的模拟包中的补丁装饰器时遇到了一些神秘的事情。

我有两个模块:

    -----test folder
          -------func.py
          -------test_test.py

在func.py中:

    def a():
        return 1

    def b():
        return a()     

在 test_test.py 中:

    import pytest
    from func import a,b
    from mock import patch,Mock

    @pytest.fixture(scope="module")
    def brands():
        return 1


    mock_b=Mock()

    @patch('test_test.b',mock_b)
    def test_compute_scores(brands):                 
         a()

补丁装饰似乎与 pytest 夹具不兼容。有人对此有见解吗?谢谢

【问题讨论】:

我遇到了类似的问题,我正在导入 from unittest.mock import patchimport mock ,我不得不删除 import mock 语句,然后停止抛出 fixture 'mocked_instance' not found 错误 我建议你切换接受的答案。 【参考方案1】:

当使用 pytest fixturemock.patch 时,测试参数顺序至关重要。

如果您在模拟的之前放置一个夹具参数:

from unittest import mock

@mock.patch('my.module.my.class')
def test_my_code(my_fixture, mocked_class):

然后模拟对象将在my_fixture 中,mocked_class 将作为夹具进行搜索:

fixture 'mocked_class' not found

但是,如果你颠倒顺序,把fixture参数放在最后:

from unittest import mock

@mock.patch('my.module.my.class')
def test_my_code(mocked_class, my_fixture):

那么一切都会好起来的。

【讨论】:

这是解决方案,而不是 Konrad 的解决方案。 这应该是解决方案【参考方案2】:

截至 Python3.3mock 模块已被拉入 unittest 库。还有一个反向移植(用于以前版本的 Python)作为独立库 mock

在同一个测试套件中组合这两个库会产生上述错误:

E       fixture 'fixture_name' not found

在您的测试套件的虚拟环境中,运行pip uninstall mock,并确保您没有在核心单元测试库旁边使用反向移植库。卸载后重新运行测试时,如果出现这种情况,您会看到 ImportErrors。

将此导入的所有实例替换为from unittest.mock import <stuff>

【讨论】:

【参考方案3】:

这并不能直接解决您的问题,但有一个 pytest-mock 插件可以让您改写:

def test_compute_scores(brands, mock):                 
     mock_b = mock.patch('test_test.b')
     a()

【讨论】:

【参考方案4】:

希望这个老问题的答案对某人有所帮助。

首先,问题不包括错误,所以我们真的不知道发生了什么。但我会尽力提供一些对我有帮助的东西。

如果你想要一个用修补对象装饰的测试,那么为了让它与 pytest 一起工作,你可以这样做:

@mock.patch('mocked.module')
def test_me(*args):
    mocked_module = args[0]

或者对于多个补丁:

@mock.patch('mocked.module1')
@mock.patch('mocked.module')
def test_me(*args):
    mocked_module1, mocked_module2 = args

pytest 正在寻找要在测试函数/方法中查找的夹具名称。提供*args 参数为我们提供了一个很好的解决查找阶段的方法。因此,要包含带有补丁的夹具,您可以这样做:

# from question
@pytest.fixture(scope="module")
def brands():
    return 1

@mock.patch('mocked.module1')
def test_me(brands, *args):
    mocked_module1 = args[0]

这对我运行 python 3.6 和 pytest 3.0.6 很有用。

【讨论】:

不幸的是,这个问题是关于在方法签名中混合 pytest 固定装置和补丁,而不仅仅是补丁。 我添加了一些关于完成问题答案的固定装置。我会说它有点老套,但它确实有效。感谢@zalpha314 指出这一点。【参考方案5】:

如果您要应用多个补丁,则注入它们的顺序很重要:

# from question
@pytest.fixture(scope="module")
def brands():
    return 1

# notice the order
@patch('my.module.my.class1')
@patch('my.module.my.class2')
def test_list_instance_elb_tg(mocked_class2, mocked_class1, brands):
    pass

【讨论】:

【参考方案6】:

我遇到了同样的问题,我的解决方案是在 1.0.1 版本中使用模拟库(在我使用 2.6.0 版本中的 unittest.mock 之前)。现在它就像一个魅力:)

【讨论】:

感谢您的回答。我在使用模拟库 1.0.1 时遇到了这个问题

以上是关于@Patch 装饰器与 pytest 夹具不兼容的主要内容,如果未能解决你的问题,请参考以下文章

如何通过自定义装饰器提供非夹具 pytest 参数?

标记一个 Pytest 夹具而不是使用该夹具的所有测试

如何参数化 Pytest 夹具

如何从范围=会话的pytest夹具返回多个值

如何在pytest中将几个参数化的夹具连接成一个新的夹具?

如何使用外部夹具跳过 pytest?