我正在使用模拟(http://www.voidspace.org.uk/python/mock/mock.html),并遇到了一个特定的模拟案例,我无法找出解决方案。
我有一个多功能调用some_function的函数正在被模拟。
def function():
some_function(1)
some_function(2)
some_function(3)
我只想嘲笑some_function的第一次和第三次调用。第二个电话我想成为真正的some_function。
我尝试了http://www.voidspace.org.uk/python/mock/mock.html#mock.Mock.mock_calls的一些替代方案,但没有成功。
提前感谢您的帮助。
答案 0 :(得分:18)
似乎wraps
参数可能是你想要的:
包装:要包装的模拟对象的项目。如果换行不是None则调用 模拟会将调用传递给包装对象(返回 实际结果并忽略return_value)。
但是,由于您只希望第二次调用不被模拟,我建议使用mock.side_effect
。
如果side_effect是可迭代的,那么每次调用mock都会返回 可迭代的下一个值。
如果您想为每次通话返回不同的值,那么它非常合适:
somefunction_mock.side_effect = [10, None, 10]
只有对somefunction
的第一次和第三次调用才会返回10.
但是,如果你确实需要调用真正的函数,而不是第二次,你也可以传递side_effect
一个可调用的函数,但我发现它很难看(可能有更聪明的做法):
class CustomMock(object):
calls = 0
def some_function(self, arg):
calls = calls + 1
if calls != 2:
return my_real_function(arg)
else:
return DEFAULT
somefunction_mock.side_effect = CustomMock().some_function
答案 1 :(得分:0)
比创建CustomMock
类还要简单:
def side_effect(*args, **kwargs):
if side_effect.counter < 10:
side_effect.counter += 1
return my_real_function(arg)
else:
return DEFAULT
side_effect.counter = 0
mocked_method.side_effect = side_effect
答案 2 :(得分:0)
我今天也面临着同样的情况。经过一番犹豫之后,我发现了另一种解决方法。
起初,我有一个函数如下:
def reboot_and_balabala(args):
os.system('do some prepare here')
os.system('reboot')
sys.exit(0)
我想调用对os.system的第一个调用,否则不会生成本地文件,并且我无法验证它。 但是我真的不想调用对os.system的第二次调用,大声笑。
起初,我的单元测试类似于:
def test_reboot_and_balabala(self):
with patch.object(os, 'system') as mock_system:
# do some mock setup on mock_system, this is what I looked for
# but I do not found any easy and clear solution
with patch.object(sys, 'exit') as mock_exit:
my_lib.reboot_and_balabala(...)
# assert mock invoke args
# check generated files
但是最后,我意识到,在调整代码之后,我可以通过以下方式获得更好的代码结构和单元测试:
def reboot():
os.system('reboot')
sys.exit(0)
def reboot_and_balabala(args):
os.system('do some prepare here')
reboot()
然后我们可以通过以下方式测试这些代码:
def test_reboot(self):
with patch.object(os, 'system') as mock_system:
with patch.object(sys, 'exit') as mock_exit:
my_lib.reboot()
mock_system.assert_called_once_with('reboot')
mock_exit.assert_called_once_with(0)
def test_reboot_and_balabala(self):
with patch.object(my_lib, 'reboot') as mock_reboot:
my_lib.reboot_and_balabala(...)
# check generated files here
mock_reboot.assert_called_once()
这不是问题的直接答案。但是我认为这很鼓舞人心。