基于输入参数模拟python函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16162015/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Mocking python function based on input arguments
提问by Juan Antonio Gomez Moriano
We have been using Mockfor python for a while.
我们使用Mockfor python 已经有一段时间了。
Now, we have a situation in which we want to mock a function
现在,我们有一种情况,我们想模拟一个函数
def foo(self, my_param):
#do something here, assign something to my_result
return my_result
Normally, the way to mock this would be (assuming foo being part of an object)
通常,模拟它的方法是(假设 foo 是对象的一部分)
self.foo = MagicMock(return_value="mocked!")
Even, if i call foo() a couple of times i can use
甚至,如果我调用 foo() 几次,我可以使用
self.foo = MagicMock(side_effect=["mocked once", "mocked twice!"])
Now, I am facing a situation in which I want to return a fixed value when the input parameter has a particular value. So if let's say "my_param" is equal to "something" then I want to return "my_cool_mock"
现在,我面临一种情况,当输入参数具有特定值时,我想返回一个固定值。所以如果让我们说“my_param”等于“something”那么我想返回“my_cool_mock”
This seems to be available on mockito for python
这似乎在mockito for python上可用
when(dummy).foo("something").thenReturn("my_cool_mock")
I have been searching on how to achieve the same with Mock with no success?
我一直在寻找如何使用 Mock 实现相同的目标但没有成功?
Any ideas?
有任何想法吗?
采纳答案by Amber
If
side_effectis a function then whatever that function returns is what calls to the mock return. Theside_effectfunction is called with the same arguments as the mock. This allows you to vary the return value of the call dynamically, based on the input:>>> 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)]
如果
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)]
回答by Juan Antonio Gomez Moriano
As indicated at Python Mock object with method called multiple times
A solution is to write my own side_effect
一个解决方案是编写我自己的 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 kwargs['foo'] == 7:
return "Foo is seven"
mockobj.mockmethod.side_effect = my_side_effect
That does the trick
这就是诀窍
回答by Shubham Chaudhary
Side effect takes a function (which can also be a lambda function), so for simple cases you may use:
副作用需要一个函数(也可以是lambda 函数),因此对于简单的情况,您可以使用:
m = MagicMock(side_effect=(lambda x: x+1))
回答by caleb
Just to show another way of doing it:
只是为了展示另一种方式:
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
回答by Novice
I know its quite an old question, might help as an improvement using python lamdba
我知道这是一个很老的问题,可能有助于使用 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
回答by Tomasz Bartkowiak
You can also use @mock.patch.object:
您还可以使用@mock.patch.object:
Let's say a module my_module.pyuses pandasto read from a database and we would like to test this module by mocking pd.read_sql_tablemethod (which takes table_nameas argument).
假设一个模块my_module.py用于pandas从数据库中读取,我们想通过模拟pd.read_sql_table方法(table_name作为参数)来测试这个模块。
What you can do is to create (inside your test) a db_mockmethod that returns different objects depending on the argument provided:
您可以做的是创建(在您的测试中)一个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
In your test function you then do:
在您的测试功能中,您可以执行以下操作:
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`
回答by Manu
I've ended up here looking for "how to mock a function based on input arguments" and I finally solved this creating a simple aux function:
我最终在这里寻找“如何根据输入参数模拟函数”,我终于解决了这个问题,创建了一个简单的辅助函数:
def mock_responses(responses, default_response=None):
return lambda input: responses[input] if input in responses else default_response
Now:
现在:
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'
Hope this will help someone!
希望这会帮助某人!
回答by Hernan
You can also use partialfrom functoolsif you want to use a function that takes parameters but the function you are mocking does not. E.g. like this:
如果您想使用带参数的函数但您模拟的函数不带参数,您也可以使用partialfrom 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))
This will return a callable that doesn't accept parameters (like Django's timezone.now()), but my mock_year function does.
这将返回一个不接受参数的可调用对象(如 Django 的 timezone.now()),但我的 mock_year 函数接受。
回答by Arseniy Panfilov
If you "want to return a fixed value when the input parameter has a particular value", maybe you don't even need a mock and could use a dictalong with its getmethod:
如果您“想要在输入参数具有特定值时返回固定值”,也许您甚至不需要模拟并且可以使用 adict及其get方法:
foo = {'input1': 'value1', 'input2': 'value2'}.get
foo('input1') # value1
foo('input2') # value2
This works well when your fake's output is a mappingof input. When it's a functionof input I'd suggest using side_effectas per Amber's answer.
当你的假的输出是输入的映射时,这很有效。当它是输入函数时,我建议side_effect按照Amber的回答使用。
You can also use a combination of both if you want to preserve Mock's capabilities (assert_called_once, call_countetc):
如果您想保留Mock的功能(等)assert_called_once,您也可以使用两者的组合call_count:
self.mock.side_effect = {'input1': 'value1', 'input2': 'value2'}.get

