(pytest) 为什么 属性 mock 在 fixture 中不起作用?

(pytest) Why doesn't property mock work in fixture?

我有一个 class 具有一些属性。在我的测试中,我需要设置一个夹具,并模拟属性。但是,patch 仅在 fixture 函数中起作用,在调用 fixture 时不起作用。知道如何解决这个问题吗?

这是问题的简化版本。假设这是我的 class Panda:

class Panda(object):
    def __init__(self, name):
        self.panda_name = name

    @property
    def name(self):
        return self.panda_name

这是我的测试

import pytest
from unittest.mock import patch, PropertyMock
from tmp import Panda


@pytest.fixture
@patch(
    'tmp.Panda.name',
    new_callable=PropertyMock,
    return_value="yuanyuan")
def fixture_panda(mk_name):
    p = Panda("this name should not matter")
    print(p.name)  # print "yuanyuan"
    return p


def test_panda_fixture(fixture_panda):
    p = fixture_panda
    print(p.name)  # print "this name should not matter"
    # this test fails
    assert p.name == "yuanyuan"

fixture_panda 中的第一个打印函数将打印 yuanyuan,这意味着 propertyMock 按预期工作。但是 test_panda_fixture 中的第二个打印函数打印 this name should not matter,这意味着 propertyMock 在这里不起作用。知道为什么会发生这种情况以及如何解决这个问题吗?

如果你想在 pytest 中进行猴子修补,你可以使用他们的 built-in fixture monkeypatch,它可以插入到所有带有 scope = function 的 fixture 中。这是我的代码库中的示例:

@pytest.fixture(scope="function", autouse=True)
def no_jwt(monkeypatch):
  """Monkeypatch the JWT verification functions for tests"""
  monkeypatch.setattr("flask_jwt_extended.verify_jwt_in_request", lambda: print("Verify"))

如果我将它应用到您的示例中,我认为这样的事情应该可行:

@pytest.fixture
def fixture_panda(monkeypatch, mk_name):
  monkeypatch.setattr('tmp.Panda.name', "yuanyuan")
  p = Panda("this name should not matter")
  print(p.name)  # print "yuanyuan"
  return p

你遇到了三个问题。首先,您正在修补夹具功能,但您应该修补测试功能。这是因为您编写它的方式,断言超出了修补的范围。

其次,你应该放弃多余的 mkname

第三,你的return_value放错地方了;它需要应用到补丁 returns 的 PropertyMock 对象,而不是作为补丁函数的参数。使用 new_callable 时,您需要在测试设置中设置它,例如:

@patch('tmp.Panda.name', new_callable=PropertyMock)
def test_panda_fixture(mock_name, fixture_panda):
    mock_name.return_value = "yuanyuan"
    ...

但是您可以在装饰器中使用 new 而不是 new_callable 来完成。这是一个显示该方法的工作版本:

import pytest
from unittest.mock import patch, PropertyMock
from tmp import Panda


@pytest.fixture
def fixture_panda():
    p = Panda("this name should not matter")
    print(p.name)  # print "yuanyuan"
    return p


@patch('tmp.Panda.name', new=PropertyMock(return_value="yuanyuan"))
def test_panda_fixture(fixture_panda):
    p = fixture_panda
    print(p.name)  # print "this name should not matter"
    # this test fails
    assert p.name == "yuanyuan"