Python模拟多个返回值

时间:2014-07-22 20:25:18

标签: python unit-testing mocking python-mock

我正在使用pythons mock.patch,并希望更改每个调用的返回值。 这是警告: 被修补的函数没有输入,因此我无法根据输入更改返回值。

这是我的代码供参考。

def get_boolean_response():
    response = io.prompt('y/n').lower()
    while response not in ('y', 'n', 'yes', 'no'):
        io.echo('Not a valid input. Try again'])
        response = io.prompt('y/n').lower()

    return response in ('y', 'yes')

我的测试代码:

@mock.patch('io')
def test_get_boolean_response(self, mock_io):
    #setup
    mock_io.prompt.return_value = ['x','y']
    result = operations.get_boolean_response()

    #test
    self.assertTrue(result)
    self.assertEqual(mock_io.prompt.call_count, 2)

io.prompt只是一个独立于平台(python 2和3)版本的"输入"。所以最终我试图模拟用户输入。我已经尝试使用列表作为返回值,但这并不能解决问题。

你可以看到,如果返回值无效,我将在这里得到一个无限循环。所以我需要一种方法来最终改变返回值,以便我的测试真正完成。

(回答这个问题的另一种可能方法是解释我如何在单元测试中模仿用户输入)


不是this question的重复,主要是因为我没有能力改变输入。

答案this question的其中一条评论是一致的,但没有提供答案/评论。

2 个答案:

答案 0 :(得分:207)

您可以将iterable分配给side_effect,每次调用时,模拟将返回序列中的下一个值:

>>> from unittest.mock import Mock
>>> m = Mock()
>>> m.side_effect = ['foo', 'bar', 'baz']
>>> m()
'foo'
>>> m()
'bar'
>>> m()
'baz'

引用Mock() documentation

  

如果 side_effect 是一个可迭代的,那么每次调用mock都会从iterable中返回下一个值。

另外,测试response is not 'y' or 'n' or 'yes' or 'no'工作;你问的是表达式(response is not 'y')是否为真,或者'y'是否为真(总是如此,非空字符串总是为真),等等or两侧的各种表达式运算符独立。见How do I test one variable against multiple values?

不要使用is来测试字符串。 CPython解释器可能重用字符串对象under certain circumstances,但这不是您应该依赖的行为。

因此,请使用:

response not in ('y', 'n', 'yes', 'no')

代替;这将使用 equality tests(==)来确定response是否引用具有相同内容(值)的字符串。

同样适用于response == 'y' or 'yes';请改用response in ('y', 'yes')

答案 1 :(得分:-2)

您还可以对多个返回值使用补丁:

@patch('Function_to_be_patched', return_value=['a', 'b', 'c'])

请记住,如果您为一个方法使用多个补丁,那么它的顺序将如下所示:

@patch('a')
@patch('b')
def test(mock_b, mock_a);
    pass

正如您在此处看到的,它将被还原。第一个提到的补丁将在最后一个位置。

相关问题