Python-当`new`参数不是DEFAULT时,为什么模拟补丁装饰器不会将模拟对象传递给测试函数

Rez*_*eza 8 python python-mock python-unittest

在 Python 3.6 中,我unittest.mock.patch用来修补这样的函数:

class SampleTest(TestCase):

    @mock.patch('some_module.f')
    def test_f(self, mocked_f):
        f()
        mocked_f.assert_called()

Run Code Online (Sandbox Code Playgroud)

This passes a mock.MagicMock() as mocked_f and everything works fine. However, when I want to use a custom mock object instead of the default mock.MagicMock() using new argument, the patch decorator does not pass the mocked object to the test_f method. Running this code will raise a TypeError:

class SampleTest(TestCase):

    @mock.patch('some_module.f', new=lambda: 8)
    def test_f(self, mocked_f):
        f()
        mocked_f.assert_called()
Run Code Online (Sandbox Code Playgroud)
TypeError: test_f() missing 1 required positional argument: 'mocked_f'
Run Code Online (Sandbox Code Playgroud)

My question is: why is this happening?

Tim*_*Tim 8

我认为模拟对象在new指定时没有传递给装饰函数的原因可能是正确的,因为您通常已经拥有对该对象的引用,因此不需要将其传递给装饰函数。

但请注意,如果您使用new_callable而不是new,则模拟对象将传递给装饰函数。这是有道理的,因为您通常不会引用从可调用函数返回的对象。

所以你可以执行如下操作:

def my_custom_mock_factory():
    my_mock = mock.Mock()
    # customisations to my_mock
    return my_mock

class SampleTest(TestCase):

    @mock.patch('some_module.f', new_callable=my_custom_mock_factory)
    def test_f(self, mocked_f):
        f()
        mocked_f.assert_called()
Run Code Online (Sandbox Code Playgroud)


che*_*ner 6

从文档(强调我的):

如果patch()用作装饰器并且省略new,则创建的模拟将作为额外参数传递给装饰函数。

通过new显式使用,装饰器不会将模拟对象作为参数传递(大概是因为它希望您已经拥有一个无需参数即可使用的引用)。

在这种情况下,解决方法是在将模拟传递给测试后对其进行配置:

class SampleTest(TestCase):

    @mock.patch('tests.f')
    def test_f(self, mocked_f):
        mocked_f.return_value = 8
        # or
        # mocked_f.side_effect = lambda: 8
        f()
        mocked_f.assert_called()
Run Code Online (Sandbox Code Playgroud)