如何测试具有多个“输入”功能的交互式 python 脚本

How to test interactive python script with multiple `input` functions

我知道如何使用单个 input 函数测试 python 函数,但我似乎无法弄清楚如何使用多个 input 测试 python 函数] 在其中发挥作用。

请参阅下面的最小示例代码 test.py

import pytest
import mock
import builtins


def unsubscribe():
    if input("are you unsubscribing? [y/n]") == "n":
        return "we are glad to have you back"
    else:
        if input("would you like a 20%% off discount code? [y/n]") == "y":
            return "your discount code is SAVE20, and we are glad you have you back"
        else:
            return "we are sad to see you go"


def test_unsubscribe():
    with mock.patch.object(builtins, 'input', lambda _: 'n'):
        assert unsubscribe() == "we are glad to have you back"
    with mock.patch.object(builtins, 'input', lambda _: 'y'):
        assert unsubscribe() == "your discount code is SAVE20, and we are glad you have you back"
    # what to put here to test the below
    #    assert unsubscribe() == "we are sad to see you go"

使用当前方法,模拟补丁将每个 input 函数替换为所有 n 或所有 y,这导致第一个输入 y 的控制流第二个输入 n 无法访问。对包含多个 input 函数的 python 函数进行单元测试的正确方法是什么?

您可以为此使用 side_effectSide_effect 将列表作为输入并按元素顺序为您提供结果。

假设您将测试代码和源代码放在不同的目录中

@patch('sample.input')
def test_options(mock_input):
   mock_input.side_effect = ['y', 'y']
   result = unsubscribe()
   assert result == "your discount code is SAVE20, and we are glad you have you back"

您可以为您的用例定义您想要的确切输出,而不是任意传递是和否值,从而将其推进到下一步

@patch('sample.input')
def test_options(mock_input):

   def input_side_effect(*args, **kwargs):
       m = {
           'are you unsubscribing? [y/n]': 'y',
           'would you like a 20%% off discount code? [y/n]': 'y',
       }.get(args[0])
       if m:
           return m
       pytest.fail('This question is not expected')

   mock_input.side_effect = input_side_effect
   result = unsubscribe()
   assert result == "your discount code is SAVE20, and we are glad you have you back"