UnitTest Python模拟只有一个函数多次调用

时间:2013-04-01 20:40:09

标签: python unit-testing mocking

我正在使用模拟(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的一些替代方案,但没有成功。

提前感谢您的帮助。

3 个答案:

答案 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()

这不是问题的直接答案。但是我认为这很鼓舞人心。