Python 如何模拟对接收可变对象作为参数的函数的调用?
举个例子:Python 如何模拟对接收可变对象作为参数的函数的调用?,python,mocking,Python,Mocking,举个例子: def func_b(a): print a def func_a(): a = [-1] for i in xrange(0, 2): a[0] = i func_b(a) 以及尝试测试函数a和模拟函数b的测试函数: import mock from mock import call def test_a(): from dataTransform.test import func_a with mock
def func_b(a):
print a
def func_a():
a = [-1]
for i in xrange(0, 2):
a[0] = i
func_b(a)
以及尝试测试函数a和模拟函数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])]
下面的工作(从unittest
导入mock
是Python 3的一部分,模块
是func\u a
和func\u b
的地方):
这继承自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])])
您可以使用new\u callable
参数将新类传递到patch
中,但是它不能与autospec
共存。请注意,您的函数使用列表调用func\u b
,因此call(0)、call(1)
必须更改为call([0])、call([1])
。当通过调用test\u a
运行时,此操作不会执行任何操作(通过)
现在我们不能同时使用new\u callable
和autospec
,因为new\u callable
是一个通用工厂,但在我们的例子中只是一个MagicMock
覆盖。但这是一个非常酷的mock
功能,我们不想失去它
我们需要的是将MagicMock
替换为ModifiedMagicMock
仅用于我们的测试:我们希望避免更改所有测试的MagicMock
行为。。。可能很危险。我们已经有了一个工具来完成它,它是patch
,与new
参数一起用于替换目标
在这种情况下,我们使用decorator来避免太多缩进并使其更具可读性:
@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])])
或:
您对
func_b()
的模拟将需要复制或以其他方式不可接触地表示它所获得的参数。然后,您可以稍后检查这些副本的值。这是否回答了您的问题?谢谢在Python2.7中,对父函数的调用应该是这样的:return super(ModifiedMagicMock,_mock_self)。_mock_call(*copy.deepcopy(args),**kwargs)对不起,我忘了。。。几个月前我就写了一个答案☺.我知道解决这个漏洞的方法。如果你同意,我可以再次编辑你的答案,否则我可以提交一个新的答案来涵盖它。我讨厌失去autospec功能。@matsjoyce我可以使用装饰器而不是语句。。。“它更可编辑。”米歇尔·阿米科,谢谢。出于某种原因(嘲笑是聪明的),只有当我颠倒了装饰者的顺序时,它才起作用。我用half decorator版本添加了半个,以防模拟的实现发生变化。
@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])])