Насколько эффективно модульное тестирование функции с несколькими входными вызовами? - PullRequest
1 голос
/ 25 мая 2019

У меня есть модульное тестирование, которое работает как задумано, однако я чувствую, что это не лучший способ протестировать несколько входов с помощью pytest. Это определенно нарушает принцип СУХОГО. Я думаю, что есть лучший способ сделать это, но я не могу понять, что. Я также не уверен, что на самом деле делать с макетом. Он не используется, но он должен быть там (см. Параметр «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. Однако, поскольку код очень повторяется, я хотел бы знать, есть ли лучший способ проверить несколько входных вызовов, как я хотел бы применить то же самое для будущих тестов.

1 Ответ

2 голосов
/ 25 мая 2019

Вы можете использовать цикл 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()

Диспетчер контекста сообщит вам, какой ввод не удался.

Является ли единственный способ сделать что-то подобное с помощью суб-теста с использованием модуля 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
...