Jua*_*ano 126 python unit-testing mocking mockito
我们一直在使用Mock for python.
现在,我们有一种情况,我们想要模拟一个函数
def foo(self, my_param):
#do something here, assign something to my_result
return my_result
Run Code Online (Sandbox Code Playgroud)
通常,模拟这个的方法是(假设foo是对象的一部分)
self.foo = MagicMock(return_value="mocked!")
Run Code Online (Sandbox Code Playgroud)
甚至,如果我打电话给foo()几次我可以使用
self.foo = MagicMock(side_effect=["mocked once", "mocked twice!"])
Run Code Online (Sandbox Code Playgroud)
现在,我面临的情况是,当输入参数具有特定值时,我想返回固定值.所以如果让我们说"my_param"等于"某事"那么我想要返回"my_cool_mock"
这似乎可以在mock上用于python
when(dummy).foo("something").thenReturn("my_cool_mock")
Run Code Online (Sandbox Code Playgroud)
我一直在寻找如何与Mock达成同样的目标并没有成功?
有任何想法吗?
Amb*_*ber 159
如果
side_effect
是一个函数,那么该函数返回的是调用mock返回的函数.使用side_effect
与mock相同的参数调用该函数.这允许您根据输入动态地改变调用的返回值:Run Code Online (Sandbox Code Playgroud)>>> 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
Jua*_*ano 35
正如Python 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
Run Code Online (Sandbox Code Playgroud)
这就是诀窍
Shu*_*ary 12
副作用需要一个函数(也可以是lambda函数),因此对于简单的情况,您可以使用:
m = MagicMock(side_effect=(lambda x: x+1))
Run Code Online (Sandbox Code Playgroud)
我最终在这里寻找“如何根据输入参数模拟函数”,我终于解决了这个问题,创建了一个简单的辅助函数:
def mock_responses(responses, default_response=None):
return lambda input: responses[input] if input in responses else default_response
Run Code Online (Sandbox Code Playgroud)
现在:
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'
Run Code Online (Sandbox Code Playgroud)
希望这会帮助某人!
如果您“想要在输入参数具有特定值时返回固定值”,也许您甚至不需要模拟并且可以使用 adict
及其get
方法:
foo = {'input1': 'value1', 'input2': 'value2'}.get
foo('input1') # value1
foo('input2') # value2
Run Code Online (Sandbox Code Playgroud)
当你的假的输出是输入的映射时,这很有效。当它是输入函数时,我建议side_effect
按照Amber的回答使用。
如果您想保留Mock
的功能(等)assert_called_once
,您也可以使用两者的组合call_count
:
self.mock.side_effect = {'input1': 'value1', 'input2': 'value2'}.get
Run Code Online (Sandbox Code Playgroud)
小智 5
如果您想使用带参数的函数但您模拟的函数不带参数,您也可以使用partial
from functools
。例如像这样:
def mock_year(year):
return datetime.datetime(year, 11, 28, tzinfo=timezone.utc)
Run Code Online (Sandbox Code Playgroud)
@patch('django.utils.timezone.now', side_effect=partial(mock_year, year=2020))
Run Code Online (Sandbox Code Playgroud)
这将返回一个不接受参数的可调用对象(如 Django 的 timezone.now()),但我的 mock_year 函数接受。
小智 5
虽然side_effect
可以达到目的,但是side_effect
为每个测试用例设置函数并不是那么方便。
我写了一个轻量级的Mock(叫做 NextMock)来增强内置的 Mock 来解决这个问题,这里是一个简单的例子:
from nextmock import Mock
m = Mock()
m.with_args(1, 2, 3).returns(123)
assert m(1, 2, 3) == 123
assert m(3, 2, 1) != 123
Run Code Online (Sandbox Code Playgroud)
它还支持参数匹配器:
from nextmock import Arg, Mock
m = Mock()
m.with_args(1, 2, Arg.Any).returns(123)
assert m(1, 2, 1) == 123
assert m(1, 2, "123") == 123
Run Code Online (Sandbox Code Playgroud)
希望这个包能让测试更愉快。随时提供任何反馈。
归档时间: |
|
查看次数: |
72492 次 |
最近记录: |