Как выполнить юнит-тестирование функции, в которой результат основан на условном выражении, содержащемся в цикле while - PullRequest
0 голосов
/ 29 мая 2019

Мне интересно, каков наилучший способ выполнить юнит-тест цикла while, который прерывается только при вводе определенного ввода (см. Код для более подробной информации).Мой тест на самом деле работает как задумано и не проходит правильно.Тем не менее, я чувствую, что это «взломано вместе» и не лучший способ проверить этот вид кода.

В настоящее время я должен вручную разорвать цикл в тесте, высмеивая пользовательский ввод, который разрывает цикл (см. Код для более подробной информации).Я пытался вам параметризировать в pytest, однако, тест застревает в бесконечном цикле, как только я высмеиваю ввод, который не прерывает цикл.Он никогда не доберется до следующего параметризованного значения.

Тестируемая функция

def export_options():
    while True:
        try:
            choice = int(input("\nPlease make a selection"))
            if choice in range(1, 5):
                return choice
            else:
                 # I want to test this line when the input is bad
                print("\nNot a valid selection\n")  
        except ValueError as err:
            print("Please enter an integer")

Тестовая функция

@mock.patch('realestate.app.user_inputs.print')
# The 1 is used to break the loop otherwise the test never ends!
@mock.patch('realestate.app.user_inputs.input', side_effect=[0, 5, 1])
def test_export_options_invalid_integer(mock_choice, mock_print):
    user_inputs.export_options()

    # Best way I could think of to test bad inputs. Anything that is less than 1 or greater than 4 will fail the test.
    # Make sure I call the correct print function
    mock_print.assert_called_with("\nNot a valid selection\n")
    # Make sure I call the correct print function twice
    assert mock_print.call_count == 2

Я получаю нужные результаты на основе моего текущего кода.Тем не менее, я хотел бы использовать лучшие практики, когда это возможно, и применять их ко всем будущим тестам при работе с циклами while, которые only ломаются на основе определенного пользовательского ввода.

1 Ответ

1 голос
/ 30 мая 2019

Относительно «настоящих» бесконечных циклов, которые никогда не завершаются: процессы во встроенных системах иногда реализуются подобным образом и завершаются только тогда, когда система выключается из-за отключения питания или из-за гибели операционной системы.Для таких циклов типичное решение состоит в том, чтобы извлечь тело цикла в отдельную функцию / метод.

def main_loop():
    while True:
        main_body()

Таким образом, вы максимизировали количество кода, который можно протестировать модулем, извлекая его в main_body.Ясно, что main_loop все еще не может быть проверен модулем, но он может быть проверен в тестах более высокого уровня.

Очевидно, что ваш цикл не такой, так как у вас есть возможность выйти из цикла без убийстватемы или тому подобное.Поэтому я бы сказал, что ваш подход к тестированию этой функции в принципе вполне обоснован.

Однако есть несколько способов улучшить набор тестов.Для примера: тестовый пример, который вы показали в качестве примера, уже является сложным тестом, и я бы рекомендовал начать с более фундаментальных тестов:

  • Проверка того, что число 1 принято
  • Проверка того, что число 4 принято
  • Проверка того, что число 0 не принято (последовательность: 0, 1 из 1..4)
  • Проверка того, что число 5 не принято(последовательность: 5, один из 1..4)

Если какой-либо из этих тестов не пройден, это даст вам более четкое указание на причину проблемы.Эти тесты еще не проверяют (в отличие от вашего теста), сможет ли подпрограмма обрабатывать более одного неверного ввода и соответствуют ли запросы и сообщения об ошибках ожидаемым.Это, однако, отдельные аспекты, и поэтому имеет смысл разделить ваш тест на отдельные тесты, один тест на аспект.

...