【发布时间】:2012-09-13 10:37:52
【问题描述】:
我使用的模拟库是...mock。
我在尝试为函数(遗留代码)编写测试用例时遇到了这个“模拟嵌套函数”问题。
这个函数使用了一个非常复杂的嵌套函数,对其他模块有很大的依赖。
我想知道是否可以使用 mock 模拟嵌套函数。
【问题讨论】:
标签: python unit-testing mocking closures
我使用的模拟库是...mock。
我在尝试为函数(遗留代码)编写测试用例时遇到了这个“模拟嵌套函数”问题。
这个函数使用了一个非常复杂的嵌套函数,对其他模块有很大的依赖。
我想知道是否可以使用 mock 模拟嵌套函数。
【问题讨论】:
标签: python unit-testing mocking closures
您是否尝试用模拟对象替换嵌套函数?如果是这样,无论函数多么复杂,这都相当简单。您可以使用 MagicMock 替换几乎任何 python 对象。
如果你需要模拟一个有返回值的函数,你只需设置MagicMock的return_value参数即可。它看起来像这样:
>>> super_nested_mock = mock.MagicMock()
>>> super_nested_mock.return_value = 42
>>> super_nested_mock()
42
但是,如果您尝试测试另一段代码,该代码在内部某处调用您的 super_nested 函数,并且想要模拟它,则需要使用 patch。在模拟库中,它看起来像这样:
with patch('super_nested') as super_nested_mock:
super_nested_mock.return_value = "A good value to test with"
assert my_function_that_calls_super_nested(5) == 20
在这里,with 块中通常调用 super_nested 的任何内容都会调用 super_nested_mock 并返回您设置的值。
您需要在补丁调用中添加的内容有些微妙。主要是,您希望修补对象,因为您正在测试的模块会看到它。更多说明请参见“where to patch”。
【讨论】:
patch无法直接定位替换。
我看到这样做的唯一方法是动态创建外部函数的副本,使用模拟函数的代码修改函数的代码对象常量:
【讨论】:
一种选择是更改您的函数,以便它可以选择接受要调用的函数,例如如果你有:
def fn_to_test():
def inner_fn():
return 1
return inner_fn() + 3
改成:
def fn_to_test( inner_fn = null )
def inner_fn_orig():
return 1
if inner_fn==null:
inner_fn = inner_fn_orig
return fn() + 3
然后“真正的”使用将获得正确的内部功能,并且在您的测试中您可以提供自己的。
fn_to_test() # calls the real inner function
def my_inner_fn():
return 3
fn_to_test( inner_fn=my_inner_fn ) # calls the new version
你也可以这样做:
def fn_to_test():
def inner_fn_orign():
return 1
inner_fn = inner_fn_orig
try:
inner_fn = fn_to_test.inner_fn
excecpt AttributeError:
pass
return inner_fn() + 3
这样你只需定义覆盖:
fn_to_test() # calls the real inner function
def my_inner_fn():
return 3
fn_to_test.inner_fn = my_inner_fn
fn_to_test() # calls the new version
【讨论】:
例如,您需要模拟来自 Google DRIVE API 的嵌套函数调用(链函数)
result = get_drive_service().files().insert(body='body', convert=True).execute()
所以你需要通过函数来打补丁:service_mock()、files()、insert(),直到最后一个execute()响应:
from mock import patch
with patch('path.to.import.get_drive_service') as service_mock:
service_mock.return_value.files.return_value.insert.\
return_value.execute.return_value = {'key': 'value', 'status': 200}
主要方案: first.return_value.second.return_value.third.return_value.last.return_value = rsp
【讨论】:
return_value 链接。
return_value,即以下也可以:first().second().third().last.return_value = rsp
return_value 处理每个嵌套方法”。