Подтвердить что ты не робот

Как поставлять входные данные stdin, files и environment в модульные тесты Python?

Как написать тесты, где возникают такие условия, как:

  • Тестовый пользовательский ввод.
  • Тестовый ввод, считанный из файла.
  • Тестовый ввод, считанный из переменной среды.

Было бы здорово, если бы кто-нибудь мог показать мне, как подойти к вышеупомянутым сценариям; все равно было бы здорово, если бы вы могли указать мне на несколько записей в документах/статьях/блогах, которые я мог бы читать.

4b9b3361

Ответ 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'