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

时间:2019-03-16 13:42:48

标签: python python-unittest python-mock

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

这将mock.MagicMock()传递为mocked_f,一切正常。但是,当我想使用自定义的模拟对象代替使用mock.MagicMock()参数的默认new时,修补程序装饰程序不会将模拟的对象传递给test_f方法。运行此代码将引发一个TypeError

class SampleTest(TestCase):

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

我的问题是:为什么会这样?

2 个答案:

答案 0 :(得分:3)

从文档中(重点是我的):

  

如果将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()

答案 1 :(得分:3)

我认为在指定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()