考虑示例:
def func_b(a):
print a
def func_a():
a = [-1]
for i in xrange(0, 2):
a[0] = i
func_b(a)
Run Code Online (Sandbox Code Playgroud)
并尝试测试 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)])
Run Code Online (Sandbox Code Playgroud)
func_a 执行后,我尝试测试 func_a 是否对 func_b 进行了正确的调用,但由于在 for 循环中我最终改变了列表,我得到:
AssertionError: Calls not found.
Expected: [call(0), call(1)]
Actual: [call([1]), call([1])]
Run Code Online (Sandbox Code Playgroud)
以下工作原理(导入mock是unittestPython 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))
Run Code Online (Sandbox Code Playgroud)
它继承自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])])
Run Code Online (Sandbox Code Playgroud)
patch您可以使用参数传递新类new_callable,但它不能与autospec. 请注意,您的函数func_b使用列表进行调用,因此call(0), call(1)必须更改为call([0]), call([1]). 当通过调用 运行时test_a,这不会执行任何操作(通过)。
现在我们不能同时使用两者new_callable,autospec因为new_callable是一个通用工厂,但在我们的例子中只是一个MagicMock覆盖。但自动识别是一个非常酷的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])])
Run Code Online (Sandbox Code Playgroud)
或者:
@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])])
Run Code Online (Sandbox Code Playgroud)