如何使用多个输入调用对功能进行单元测试的效率如何?

时间:2019-05-24 22:27:21

标签: python-3.x unit-testing input mocking pytest

我有一个可以按预期工作的单元测试,但是我觉得这不是用pytest测试多个输入的最佳方法。它绝对违反了DRY原则。我认为有一个更好的方法可以解决此问题,但我不知道该怎么办。我也不确定该模拟的实际用途。它未使用,但必须存在(请参见下面代码中函数中的“ mock_choice”参数)。

我认为遍历所有呼叫可能会奏效,但并没有按预期进行。除了使用side_effects并调用四次以确保获得预期的返回值外,我真的找不到其他方法。

要测试的功能

def export_options():
    while True:
        try:
            choice = int(input("\nPlease make a selection"))
            if choice in ([option for option in range(1, 5)]):
                return choice  # This what I'm testing
            else:
                print("\nNot a valid selection\n")
        except ValueError as err:
            print("Please enter an integer")

测试功能

@mock.patch('realestate.app.user_inputs.input', side_effect=[1, 2, 3, 4])
def test_export_options_valid_choice(mock_choice): # mock_choice needs to be here but isn't used!

    export_option = user_inputs.export_options()
    assert export_option == 1

    export_option = user_inputs.export_options()
    assert export_option == 2

    export_option = user_inputs.export_options()
    assert export_option == 3

    export_option = user_inputs.export_options()
    assert export_option == 4

测试有效。它在函数返回介于1和4之间的所有值时通过。但是,由于代码非常重复,因此我想知道是否有更好的方法来测试多个输入调用,因为我想将其应用于以后的测试。 / p>

1 个答案:

答案 0 :(得分:2)

您可以使用for循环来避免重复代码。

@mock.patch('realestate.app.user_inputs.input')
def test_export_options_valid_choice(self, mock_choice):
    for response in [1, 2, 3, 4]:
        with self.subTest(response=response)
            mock_choice.return_value = response
            export_option = user_inputs.export_options()
            self.assertEqual(export_option, response)
            # Not in original version, but other tests might need it.
            mock_choice.assert_called_once_with("\nPlease make a selection")
            mock_choice.reset_mock()

subtest context manager会告诉您哪个输入失败。

  

做这样的事情的唯一方法是使用unittest模块进行子测试吗?我知道pytest不支持子测试,但我希望存在类似的黑客行为。

您当然可以在不使用子测试的情况下进行循环,但是您可能很难分辨出哪个输入失败。更一般地,您可以为每个测试调用一个通用的辅助函数,而不是循环。

特别是对于pytest,您可以使用@pytest.mark.parametrize装饰器来自动执行此操作。

@pytest.mark.parametrize('response', [1, 2, 3, 4])
@mock.patch('realestate.app.user_inputs.input')
def test_export_options_valid_choice(mock_choice, response):
    mock_choice.return_value = response
    export_option = user_inputs.export_options()
    assert export_option == response