Python 模拟多个返回值

348 投票
4 回答
271916 浏览
提问于 2025-04-18 14:20

我正在使用Python的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中使用,类似于“input”。所以我最终是想模拟用户的输入。我尝试过用一个列表来设置返回值,但似乎不太管用。

你可以看到,如果返回值是无效的,我会在这里陷入无限循环。所以我需要一种方法来最终改变返回值,以便我的测试能够顺利结束。

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


这不是这个问题的重复,主要是因为我无法改变输入。

这个问题的回答中有一个评论提到类似的内容,但没有提供答案或评论。

4 个回答

-9

你还可以使用patch来处理多个返回值:

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

记住,如果你对一个方法使用了多个patch,那么它们的顺序会是这样的:

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

如你所见,顺序会被反转。最先提到的patch会放在最后的位置。

0

我觉得最简单的解决办法是结合使用 iter()patch()side_effect 这几个东西。

from unittest.mock import patch
from return_inputs import *

def test_something_that_has_2_inputs(self):
        input_list = ['Foo', 'Bar']
        inputs = iter(input_list)
        with patch("builtins.input", side_effect=inputs):
            name1, name2 = return_inputs()
        assert name1 == "Foo"
        assert name2 == 'Bar'
17

对于多个返回值,我们可以在初始化补丁时使用side_effect,并将可迭代对象传递给它。

sample.py

def hello_world():
    pass

test_sample.py

from unittest.mock import patch
from sample import hello_world

@patch('sample.hello_world', side_effect=[{'a': 1, 'b': 2}, {'a': 4, 'b': 5}])
def test_first_test(self, hello_world_patched):
    assert hello_world() == {'a': 1, 'b': 2}
    assert hello_world() == {'a': 4, 'b': 5}
    assert hello_world_patched.call_count == 2
586

你可以把一个可迭代对象赋值给side_effect,这样每次调用这个模拟对象时,它就会返回序列中的下一个值:

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

引用Mock()的文档

如果side_effect是一个可迭代对象,那么每次调用这个模拟对象时,它会返回可迭代对象中的下一个值。

撰写回答