我们在python中使用Mock已经有一段时间了。
现在,我们有这样一种情况,我们想模拟一个函数
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!"])
现在,我面临这样一种情况:当输入参数具有特定值时,我想返回一个固定值。如果my_param等于something那么我要返回my_cool_mock
这似乎在python的mockito上可用
when(dummy).foo("something").thenReturn("my_cool_mock")
我一直在寻找如何实现同样的Mock没有成功?
什么好主意吗?
如果你想使用一个带参数的函数,而你要模拟的函数不带参数,你也可以使用来自functools的partial。例如:
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函数可以。
虽然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
它还支持参数匹配器:
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
希望这个包能让测试更愉快。请随时提供任何反馈。
我最终在这里寻找“如何基于输入参数模拟一个函数”,我最终解决了这个问题,创建了一个简单的aux函数:
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'
希望这能帮助到一些人!