如何模拟调用接收可变对象作为参数的函数?

问题描述 投票:0回答:2

考虑示例:

def func_b(a):
    print a

def func_a():
    a = [-1]
    for i in xrange(0, 2):
        a[0] = i
        func_b(a)

以及尝试测试 func_a 和模拟 func_b 的测试函数:

import mock
from mock import call

def test_a():
    from dataTransform.test import func_a
    with mock.patch('dataTransform.test.func_b', autospec=True) as func_b_mock:
        func_a()
        func_b_mock.assert_has_calls([call(0), call(1)])

func_a 执行后,我尝试测试 func_a 是否对 func_b 进行了正确的调用,但由于在 for 循环中,我最终改变了列表,我得到:

AssertionError: Calls not found.
Expected: [call(0), call(1)]
Actual: [call([1]), call([1])]
python mocking
2个回答
3
投票

以下工作(从

mock
导入
unittest
是 Python 3 的事情,
module
func_a
func_b
所在的位置):

import mock
from mock import call
import copy

class ModifiedMagicMock(mock.MagicMock):
    def _mock_call(_mock_self, *args, **kwargs):
        return super(ModifiedMagicMock, _mock_self)._mock_call(*copy.deepcopy(args), **copy.deepcopy(kwargs))

它继承自

MagicMock
,并重新定义了调用行为以深度复制参数和关键字参数。

def test_a():
    from module import func_a
    with mock.patch('module.func_b', new_callable=ModifiedMagicMock) as func_b_mock:
        func_a()
        func_b_mock.assert_has_calls([call([0]), call([1])])

您可以使用

patch
参数将新类传递到
new_callable
,但它不能与
autospec
共存。请注意,您的函数使用列表调用
func_b
,因此
call(0), call(1)
必须更改为
call([0]), call([1])
。当通过调用
test_a
运行时,这不会执行任何操作(通过)。

现在我们不能同时使用

new_callable
autospec
,因为
new_callable
是一个通用工厂,但在我们的例子中只是一个
MagicMock
覆盖。但是 Autospeccing 是一个非常酷的
mock
功能,我们不想失去它。

我们需要的只是为了我们的测试而将

MagicMock
替换为
ModifiedMagicMock
:我们希望避免改变所有测试的
MagicMock
行为...可能很危险。我们已经有一个工具可以做到这一点,它是
patch
,与
new
参数一起使用来替换目的地。

在这种情况下,我们使用装饰器来避免过多的缩进并使其更具可读性:

@mock.patch('module.func_b', autospec=True)
@mock.patch("mock.MagicMock", new=ModifiedMagicMock)
def test_a(func_b_mock):
    from module import func_a
    func_a()
    func_b_mock.assert_has_calls([call([0]), call([1])])

或者:

@mock.patch("mock.MagicMock", new=ModifiedMagicMock)
def test_a():
    with mock.patch('module.func_b') as func_b_mock:
        from module import func_a
        func_a()
        func_b_mock.assert_has_calls([call([0]), call([1])])

0
投票

Python 文档中现在有一个关于此主题的部分,其中包含多种建议的解决方法:https://docs.python.org/3/library/unittest.mock-examples.html#coping-with-mutable-arguments

© www.soinside.com 2019 - 2024. All rights reserved.