Есть ли способ получить доступ к исходной функции в издеваемом методе/функции, чтобы я мог изменять аргументы и передавать их исходным функциям?
Я хотел бы изменить аргументы, переданные методу в модуле, в отличие от замены его возвращаемого значения.
Я нашел способ обойти это, но он кажется чем-то полезным и превратился в урок в насмешек.
module.py
from third_party import ThirdPartyClass
ThirdPartyClass.do_something('foo', 'bar')
ThirdPartyClass.do_something('foo', 'baz')
tests.py
@mock.patch('module.ThirdPartyClass.do_something')
def test(do_something):
# Instead of directly overriding its return value
# I'd like to modify the arguments passed to this function.
# change return value, no matter inputs
do_something.return_value = 'foo'
# change return value, based on inputs, but have no access to the original function
do_something.side_effect = lambda x, y: y, x
# how can I wrap do_something, so that I can modify its inputs and pass it back to the original function?
# much like a decorator?
Я пробовал что-то вроде следующего, но не только он повторяющийся и уродливый, он не работает. После некоторой интроспекции PDB. Мне интересно, просто ли это из-за того, что эта сторонняя библиотека работает, так как я вижу, как оригинальные функции вызываются успешно, когда я бросаю pdb внутри side_effect
.
Либо это, либо какая-то автоматическая насмешка магии, я просто не слежу за тем, что мне очень хотелось бы узнать.
def test():
from third_party import ThirdPartyClass
original_do_something = ThirdPartyClass.do_something
with mock.patch('module.ThirdPartyClass.do_something' as mocked_do_something:
def side_effect(arg1, arg2):
return original_do_something(arg1, 'overridden')
mocked_do_something.side_effect = side_effect
# execute module.py
Приветствуется любое руководство!
Ответы
Ответ 1
Вы можете использовать параметр wraps
для макетного вызова. (Docs для справки.) Таким образом будет вызываться оригинальная функция, но она будет иметь все, что есть в интерфейсе Mock.
Итак, для изменения параметров, вызываемых в исходную функцию, вы можете попробовать это следующим образом:
#file org.py
def func(x):
print(x)
#file main.py
from unittest import mock
import org
of = org.func
def wrapped(a):
of('--{}--'.format(a))
with mock.patch('org.func', wraps=wrapped):
org.func('x')
org.func.assert_called_with('x')
результат:
--x--
Ответ 2
Хитрость заключается в том, чтобы передать исходную базовую функцию, к которой вы все еще хотите получить доступ, в качестве параметра функции.
Например, для тестирования условий гонки, tempfile.mktemp
возвращает существующее имя пути:
def mock_mktemp(*, orig_mktemp=tempfile.mktemp, **kwargs):
"""Ensure mktemp returns an existing pathname."""
temp = orig_mktemp(**kwargs)
open(temp, 'w').close()
return temp
Выше, orig_mktemp
оценивается, когда функция объявлена, а не когда она вызывается, поэтому все вызовы будут иметь доступ к исходному методу tempfile.mktemp
через orig_mktemp
.
Я использовал это следующим образом:
@unittest.mock.patch('tempfile.mktemp', side_effect=mock_mktemp)
def test_retry_on_existing_temp_path(self, mock_mktemp):
# Simulate race condition: creation of temp path after tempfile.mktemp
...