Python模拟多个返回值


168

我正在使用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)。因此,最终我将尝试模拟用户的输入。我已经尝试过使用列表作为返回值,但这并不能正常工作。

您可以看到,如果返回值无效,那么我将在此处得到一个无限循环。因此,我需要一种最终更改返回值的方法,以便测试实际上完成。

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


不是这个问题的重复,主要是因为我没有能力改变输入。

关于这个问题的答案的评论之一是相同的,但是没有提供答案/评论。


3
response is not 'y' or 'n' or 'yes' or 'no'没有做什么,你认为它。请参阅如何针对多个值测试一个变量?你应该使用is来比较两个字符串值,使用==比较,而不是对象的身份。
马丁·皮特斯

在这里也要小心。似乎您正在尝试is比较字符串文字。不要那样做 它有时起作用的事实只是CPython中的一个实现细节。另外,response is not 'y' or 'n' or 'yes' or 'no'可能没有按照您的想法做……
mgilson 2014年

Answers:


300

你可以指定一个迭代side_effect,并模拟将序列中的每个被调用时,返回的下一个值:

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

引用Mock()文档

如果side_effect是可迭代的,则对模拟的每次调用都将返回可迭代的下一个值。

顺便说一句,测试response is not 'y' or 'n' or 'yes' or 'no'无法进行;您在问表达式(response is not 'y')是正确的还是'y'正确的(总是这样,非空字符串始终为true),等等。or运算符两侧的各种表达式都是独立的。请参阅如何针对多个值测试一个变量?

你应该不会使用is,以测试对一个字符串。CPython解释器在某些情况下可以重用字符串对象,但这不是您应该依靠的行为。

因此,请使用:

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

代替; 这将使用相等性测试(==)确定是否response引用了具有相同内容(值)的字符串。

同样适用于response == 'y' or 'yes'; 使用response in ('y', 'yes')代替。


有没有办法用标准做到这一点mock?有没有办法像我在使用标准模拟一样在MagicMock中使用补丁?
尼克·休里希

@Humdinger:这是stardard Mock类的功能。
马丁·彼得

17
分配列表似乎仅适用于python 3。使用python 2.7进行测试,我需要使用迭代器代替(m.side_effect = iter(['foo', 'bar', 'baz']))。
user686249

1
@ user686249:我确实可以重现此内容,因为从方法指定产生一个lambda(函数),而不是MagicMock。函数对象不能具有的特性,所以side_effect属性具有是一个迭代。不过,您不应该指定这种方法。更好的使用mock.patch.object(requests.Session, 'post'); 会导致修补程序对象正确地对方法进行自动指定side_effect正确支持。
马丁·彼得斯

3
@ JoeMjr2:迭代器用尽时,StopIteration引发。您可以使用任何迭代器,因此可以使用一次itertools.chain(['Foo'], itertools.repeat('Bar'))生产Foo,然后永远生产Bar
马丁·彼得斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.