Как издеваться над вложенными функциями?

Издевательская библиотека, которую я использую, является... mock.

Я столкнулся с этой проблемой "ложных вложенных функций", когда пытался написать тестовый пример для функции (устаревший код).

Эта функция использовала очень сложную вложенную функцию с большими зависимостями от других модулей.

Интересно, можно ли издеваться над вложенными функциями с помощью mock.

Ответ 1

Например, вам нужно высмеять вложенные вызовы функций (связанные функции) из API Google DRIVE

result = get_drive_service().files().insert(body='body', convert=True).execute()   

поэтому вам необходимо выполнить патчи через функции: service_mock(), files(), insert(), до последнего выполнения():

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}

Основная схема: return_value.second. return_value.third. return_value.last. return_value= rsp

Ответ 2

Один из вариантов - изменить вашу функцию, чтобы она могла принимать функцию для вызова, например. если у вас есть:

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

Ответ 3

Вы пытаетесь заменить вложенную функцию макетным объектом? Если да, то это довольно просто, независимо от того, насколько сложна функция. Вы можете использовать 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 и просто вернуть значение, которое вы ему установили.

Есть какая-то тонкость в том, что именно вам нужно включить в вызов патча. В основном, вы хотите исправить объект, поскольку модуль, который вы тестируете, увидит его. См. " где исправлять" для получения дополнительной инструкции.