Pytest: как проверить функцию с помощью входного вызова?
у меня есть консольная программа, написанная на Python. Он задает пользователю вопросы, используя команду:
some_input = input('Answer the question:', ...)
как бы я проверил функцию, содержащую вызов input
С помощью pytest
?
Я бы не хотел заставлять тестировщика вводить текст много раз только для завершения одного тестового запуска.
4 ответов
вы, вероятно, должны издеваться над встроенным input
функция, вы можете использовать teardown
функциональность pytest
чтобы вернуться к исходному input
функции после каждого испытания.
import module # The module which contains the call to input
class TestClass:
def test_function_1(self):
# Override the Python built-in input method
module.input = lambda: 'some_input'
# Call the function you would like to test (which uses input)
output = module.function()
assert output == 'expected_output'
def test_function_2(self):
module.input = lambda: 'some_other_input'
output = module.function()
assert output == 'another_expected_output'
def teardown_method(self, method):
# This method is being called after each test case, and it will revert input back to original function
module.input = input
более элегантным решением было бы использовать mock
вместе с with statement
. Таким образом, вам не нужно использовать teardown, и исправленный метод будет жить только в with
масштаб.
import mock
import module
def test_function():
with mock.patch.object(__builtin__, 'input', lambda: 'some_input'):
assert module.function() == 'expected_output'
как предложил компилятор, у pytest есть новое приспособление monkeypatch для этого. А monkeypatch
вы можете заменить sys.stdin
С некоторыми пользовательскими текст IO, например, ввод из файла или буфера StringIO в памяти:
import sys
class Test:
def test_function(self):
sys.stdin = open("preprogrammed_inputs.txt")
module.call_function()
def setup_method(self):
self.orig_stdin = sys.stdin
def teardown_method(self):
sys.stdin = self.orig_stdin
это более надежно, чем только исправление input()
, так как этого будет недостаточно, если модуль использует любые другие методы потребления текста из stdin.
Это также можно сделать довольно элегантно с помощью пользовательского контекстного менеджера
import sys
from contextlib import contextmanager
@contextmanager
def replace_stdin(target):
orig = sys.stdin
sys.stdin = target
yield
sys.stdin = orig
а затем просто используйте его так, например:
with replace_stdin(StringIO("some preprogrammed input")):
module.call_function()
вы можете сделать это с помощью mock.patch
следующим образом.
во-первых, в вашем коде создайте фиктивную функцию для вызовов input
:
def __get_input(text):
return input(text)
в ваших тестовых функциях:
import my_module
from mock import patch
@patch('my_module.__get_input', return_value='y')
def test_what_happens_when_answering_yes(self, mock):
"""
Test what happens when user input is 'y'
"""
# whatever your test function does
например, если у вас есть цикл, проверяющий, что единственные допустимые ответы находятся в ['y', 'Y', 'n', 'N'], вы можете проверить, что ничего не происходит при вводе другого значения.
в этом случае мы предположим, что
SystemExit
возникает при ответе 'N':
@patch('my_module.__get_input')
def test_invalid_answer_remains_in_loop(self, mock):
"""
Test nothing's broken when answer is not ['Y', 'y', 'N', 'n']
"""
with self.assertRaises(SystemExit):
mock.side_effect = ['k', 'l', 'yeah', 'N']
# call to our function asking for input