Как поставлять входные данные stdin, files и environment в модульные тесты Python?
Как написать тесты, где возникают такие условия, как:
- Тестовый пользовательский ввод.
- Тестовый ввод, считанный из файла.
- Тестовый ввод, считанный из переменной среды.
Было бы здорово, если бы кто-нибудь мог показать мне, как подойти к вышеупомянутым сценариям; все равно было бы здорово, если бы вы могли указать мне на несколько записей в документах/статьях/блогах, которые я мог бы
читать.
Ответы
Ответ 1
Все три описанные вами ситуации - это то, где вам нужно специально уйти с пути, чтобы убедиться, что вы используете свободную муфту в своем дизайне.
Вам действительно нужен метод unit test Python raw_input
? Метод open
? os.environ.get
? Нет.
Вам нужно настроить свой дизайн, чтобы вы могли заменить другие способы получения этого ввода. Затем во время тестирования вашего устройства вы нажмете заглушку, которая на самом деле не называет raw_input
или open
.
Например, ваш обычный код может выглядеть примерно так:
import os
def say_hello(input_func):
name = input_func()
return "Hello " + name
def prompt_for_name():
return raw_input("What is your name? ")
print say_hello(prompt_for_name)
# Normally would pass in methods, but lambdas can be used for brevity
print say_hello(lambda: open("a.txt").readline())
print say_hello(lambda: os.environ.get("USER"))
Сессия выглядит так:
What is your name? somebody
Hello somebody
Hello [some text]
Hello mark
Тогда ваш тест будет выглядеть следующим образом:
def test_say_hello():
output = say_hello(lambda: "test")
assert(output == "Hello test")
Имейте в виду, что вам не нужно проверять языковые возможности ввода-вывода (если только вы не разрабатываете язык, это совсем другая ситуация).
Ответ 2
Если вы привязаны к использованию raw_input (или любого другого конкретного источника ввода), я являюсь большим сторонником библиотеки mock. Учитывая код, который использовал Марк Рушаков в своем примере:
def say_hello():
name = raw_input("What is your name? ")
return "Hello " + name
Ваш тестовый код может использовать mock:
import mock
def test_say_hello():
with mock.patch('__builtin__.raw_input', return_value='dbw'):
assert say_hello() == 'Hello dbw'
with mock.patch('__builtin__.raw_input', side_effect=['dbw', 'uki']):
assert say_hello() == 'Hello dbw'
assert say_hello() == 'Hello uki'
Эти утверждения пройдут. Обратите внимание, что side_effect возвращает элементы списка по порядку. Это может сделать намного больше! Я бы рекомендовал проверить документацию.
Ответ 3
Если вы можете уйти без использования внешнего процесса, сделайте это.
Однако есть ситуации, когда это сложно, и вы действительно хотите использовать процесс, например, вы хотите протестировать интерфейс командной строки исполняемого файла C.
Пользовательский ввод
Используйте subprocess.Popen
как в:
process = subprocess.Popen(
command,
shell = False,
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
universal_newlines = True
)
stdout, stderr = process.communicate("the user input\nline 2")
exit_status = process.wait()
Нет никакой разницы между принятием ввода от пользователя и взятием его из канала для ввода, выполненного с помощью таких методов, как raw_input
или sys.stdin.read()
.
Файлы
-
Создайте временный каталог и создайте файлы, которые вы хотите прочитать, в тестовых методах setUp
:
tdir = tempfile.mkdtemp(
prefix = 'filetest_',
)
fpath = os.path.join(tdir,'filename')
fp = open(fpath, 'w')
fp.write("contents")
fp.close()
-
Прочитайте файл в тестах.
-
Затем удалите временную директорию.
shutil.rmtree(tdir)
-
Это довольно сложное чтение из файлов, и большинство программ могут читать либо из файлов, либо из STDIN (например, с помощью fileinput
). Итак, если вы хотите протестировать то, что происходит, когда определенный контент вводится, и ваша программа принимает STDIN, просто используйте Popen
для тестирования программы.
Переменные окружения
- Задайте переменные среды
os.environ["THE_VAR"] = "the_val"
- Отсоедините их от
del os.environ["THE_VAR"]
-
os.environ = {'a':'b'}
не работает
- Затем вызовите
subprocess.Popen
. Окружение наследуется от вызывающего процесса.
Код шаблона
У меня есть модуль на мой github, который проверяет STDOUT
, STDERR
и статус выхода, указанный STDIN
, аргументы командной строки и окружающей среды. Также проверьте тесты для этого модуля в разделе "тесты". Для этого должны быть намного лучшие модули, поэтому используйте мои только для учебных целей.
Ответ 4
Используя pytest:
import os
def test_user_input(monkeypatch):
inputs = [10, 'y']
input_generator = (i for i in inputs)
monkeypatch.setattr('__builtin__.raw_input', lambda prompt: next(input_generator))
assert raw_input('how many?') == 10
assert raw_input('you sure?') == 'y'
def test_file_input(tmpdir):
fixture = tmpdir.join('fixture.txt')
fixture.write(os.linesep.join(['1', '2', '3']))
fixture_path = str(fixture.realpath())
with open(fixture_path) as f:
assert f.readline() == '1' + os.linesep
def test_environment_input(monkeypatch):
monkeypatch.setenv('STAGING', 1)
assert os.environ['STAGING'] == '1'