Смысл функции python, основанный на входных аргументах
Мы использовали Mock для python какое-то время.
Теперь у нас есть ситуация, когда мы хотим издеваться над функцией
def foo(self, my_param):
#do something here, assign something to my_result
return my_result
Как правило, способ издеваться над этим был бы (предполагая, что foo является частью объекта)
self.foo = MagicMock(return_value="mocked!")
Даже если я вызываю foo() пару раз, я могу использовать
self.foo = MagicMock(side_effect=["mocked once", "mocked twice!"])
Теперь я сталкиваюсь с ситуацией, в которой я хочу вернуть фиксированное значение, когда входной параметр имеет определенное значение. Так что если пусть say "my_param" равен "что-то", тогда я хочу вернуть "my_cool_mock"
Это похоже на mockito для python
when(dummy).foo("something").thenReturn("my_cool_mock")
Я искал, как добиться успеха с Mock без успеха?
Любые идеи?
Ответы
Ответ 1
Если side_effect
- это функция, то любая возвращаемая функция что вызывает возврат к макету. Функция side_effect
вызывается с те же аргументы, что и макет. Это позволяет варьировать возврат значение динамического вызова на основе ввода:
>>> def side_effect(value):
... return value + 1
...
>>> m = MagicMock(side_effect=side_effect)
>>> m(1)
2
>>> m(2)
3
>>> m.mock_calls
[call(1), call(2)]
http://www.voidspace.org.uk/python/mock/mock.html#calling
Ответ 2
Как указано в Pockon Mock объект с методом, называемым несколько раз
Решение состоит в том, чтобы написать свой собственный side_effect
def my_side_effect(*args, **kwargs):
if args[0] == 42:
return "Called with 42"
elif args[0] == 43:
return "Called with 43"
elif kwarg['foo'] == 7:
return "Foo is seven"
mockobj.mockmethod.side_effect = my_side_effect
Это трюк
Ответ 3
Побочный эффект принимает функцию (которая также может быть лямбда-функцией), поэтому для простых случаев вы можете использовать:
m = MagicMock(side_effect=(lambda x: x+1))
Ответ 4
Я закончил здесь поиском "как смоделировать функцию на основе входных аргументов" и наконец решил эту проблему, создав простую вспомогательную функцию:
def mock_responses(responses, default_response=None):
return lambda input: responses[input] if input in responses else default_response
Сейчас:
my_mock.foo.side_effect = mock_responses({'x': 42, 'y': [1,2,3]})
my_mock.goo.side_effect = mock_responses({'hello': 'world'},
default_response='hi')
...
my_mock.foo('x') # => 42
my_mock.foo('y') # => [1,2,3]
my_mock.foo('unknown') # => None
my_mock.goo('hello') # => 'world'
my_mock.goo('ey') # => 'hi'
Надеюсь, это кому-нибудь поможет!
Ответ 5
Просто, чтобы показать другой способ сделать это:
def mock_isdir(path):
return path in ['/var/log', '/var/log/apache2', '/var/log/tomcat']
with mock.patch('os.path.isdir') as os_path_isdir:
os_path_isdir.side_effect = mock_isdir
Ответ 6
Вы также можете использовать @mock.patch.object
:
Допустим, модуль my_module.py
использует pandas
для чтения из базы данных, и мы хотели бы протестировать этот модуль с pd.read_sql_table
метода pd.read_sql_table
(который принимает аргумент table_name
).
Что вы можете сделать, это создать (внутри вашего теста) метод db_mock
который возвращает разные объекты в зависимости от предоставленного аргумента:
def db_mock(**kwargs):
if kwargs['table_name'] == 'table_1':
# return some DataFrame
elif kwargs['table_name'] == 'table_2':
# return some other DataFrame
В своей тестовой функции вы затем делаете:
import my_module as my_module_imported
@mock.patch.object(my_module_imported.pd, "read_sql_table", new_callable=lambda: db_mock)
def test_my_module(mock_read_sql_table):
# You can now test any methods from 'my_module', e.g. 'foo' and any call this
# method does to 'read_sql_table' will be mocked by 'db_mock', e.g.
ret = my_module_imported.foo(table_name='table_1')
# 'ret' is some DataFrame returned by 'db_mock'
Ответ 7
Вы также можете использовать partial
из functools
, если вы хотите использовать функцию, которая принимает параметры, а функция, над которой вы работаете, - нет. Например. как это:
def mock_year(year):
return datetime.datetime(year, 11, 28, tzinfo=timezone.utc)
@patch('django.utils.timezone.now', side_effect=partial(mock_year, year=2020))
Это вернет вызываемый объект, который не принимает параметры (как Django timezone.now()), но моя функция mock_year принимает.
Ответ 8
Я знаю, это довольно старый вопрос, может помочь как улучшение, используя Python Lamdba
self.some_service.foo.side_effect = lambda *args:"Called with 42" \
if args[0] == 42 \
else "Called with 42" if args[0] == 43 \
else "Called with 43" if args[0] == 43 \
else "Called with 45" if args[0] == 45 \
else "Called with 49" if args[0] == 49 else None