@Patch 装饰器与 pytest fixture 不兼容

70 投票
7 回答
47124 浏览
提问于 2025-04-18 15:29

我遇到了一些神秘的事情,就是在使用mock包里的patch装饰器和pytest的fixture结合时,出现了一些问题。

我有两个模块:

    -----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()

看起来patch装饰器和pytest的fixture不太兼容。有没有人对此有见解?谢谢!

7 个回答

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

Python3.3开始,mock模块已经并入了unittest库。如果你使用的是旧版本的Python,还有一个独立的mock库可以使用。

如果在同一个测试套件中同时使用这两个库,就会出现上面提到的错误:

E       fixture 'fixture_name' not found

在你的测试套件的虚拟环境中,运行pip uninstall mock,确保你没有同时使用旧版的库和核心的unittest库。如果你在卸载后重新运行测试,可能会看到ImportError的错误信息。

把所有的导入语句替换成from unittest.mock import <stuff>

7

希望这个对旧问题的回答能帮助到某个人。

首先,这个问题没有包含错误信息,所以我们并不真的知道发生了什么。不过我会尽量提供一些对我有帮助的内容。

如果你想要一个带有修补对象的测试,那么为了让它在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会查找测试函数或方法中的fixture名称。提供*args参数可以很好地解决查找阶段的问题。所以,如果你想要包含一个带修补的fixture,你可以这样做:

# 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时,这个方法对我有效。

150

在使用pytest的fixturemock.patch时,测试参数的顺序非常重要。

如果你把一个fixture参数放在一个被模拟的参数之前:

from unittest import mock

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

那么模拟对象会在my_fixture里,而mocked_class会被当作一个fixture来查找:

fixture 'mocked_class' not found

但是,如果你把顺序反过来,把fixture参数放在最后:

from unittest import mock

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

那么一切都会正常。

-7

我也遇到过同样的问题,解决办法是使用1.0.1版本的mock库(之前我用的是2.6.0版本的unittest.mock)。现在一切都正常了,真是太好了 :)

撰写回答