如何模拟嵌套函数?

sat*_*oru 21 python closures unit-testing mocking

我使用的模拟库是...... mock.

当我尝试为函数(遗留代码)编写测试用例时,我遇到了这个"模拟嵌套函数"问题.

此函数使用了非常复杂的嵌套函数,并且对其他模块具有很强的依赖性.

我想知道是否有可能用它来模拟嵌套函数mock.

pym*_*men 32

例如,您需要从Google DRIVE API模拟嵌套函数调用(链接函数)

result = get_drive_service().files().insert(body='body', convert=True).execute()   
Run Code Online (Sandbox Code Playgroud)

所以你需要修补函数:service_mock(),files(),insert(),直到last 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}
Run Code Online (Sandbox Code Playgroud)

主要方案:第一.return_value .second.return_value.第三.return_value .last.return_value = rsp

  • 除了最后一个元素之外,不需要对所有元素使用“return_value”,即以下内容也可以:“first().second().third().last.return_value = rsp” (3认同)
  • 这节省了我的周末.请注意,嵌套属性不需要与`return_value`链接. (2认同)
  • 我对这种方法的问题是,在模拟内部嵌套函数之前,它必须首先模拟外部服务函数。有时,我们只想模拟内部函数本身。 (2认同)

Mic*_*son 5

一种选择是更改函数,以便它有选择地接受要调用的函数,例如,如果您具有:

def fn_to_test():
  def inner_fn():
    return 1
  return inner_fn() + 3
Run Code Online (Sandbox Code Playgroud)

更改为:

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
Run Code Online (Sandbox Code Playgroud)

然后,“实际”使用将获得正确的内部功能,并且在测试中您可以提供自己的功能。

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
Run Code Online (Sandbox Code Playgroud)

您也可以这样做:

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
Run Code Online (Sandbox Code Playgroud)

这样,您只需定义覆盖:

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
Run Code Online (Sandbox Code Playgroud)


Wil*_*uck -2

您是否尝试用模拟对象替换嵌套函数?如果是这样,那么无论函数有多复杂,那就相当简单了。您可以使用MagicMock来替换几乎任何 python 对象。

如果您需要模拟一个返回某些内容的函数,您只需设置MagicMocksreturn_value参数即可。它看起来像这样:

>>> super_nested_mock = mock.MagicMock()
>>> super_nested_mock.return_value = 42
>>> super_nested_mock()
42
Run Code Online (Sandbox Code Playgroud)

但是,如果您尝试测试另一段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
Run Code Online (Sandbox Code Playgroud)

在这里,with块中通常调用的任何内容super_nested都会调用super_nested_mock并仅返回您为其设置的值。

您到底需要在补丁调用中添加什么内容有一些微妙之处。主要是,您想要修补该对象,因为您正在测试的模块会看到它。有关更多说明,请参阅“修补位置”。

  • 这不起作用,因为嵌套函数仅存在于我要测试的函数中。所以`patch`无法直接定位并替换它。 (3认同)