考虑例子:
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和mocks 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])]
答案 0 :(得分:2)
以下工作(来自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])])