Как передать входные данные из stdin, файлов и переменных среды в модульные тесты Python? - PullRequest
28 голосов
/ 11 апреля 2010

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

  1. Проверка пользовательского ввода.
  2. Проверка ввода, прочитанного из файла.
  3. Проверка ввода, считанного из переменной среды.

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

Ответы [ 4 ]

31 голосов
/ 11 апреля 2010

Все три ситуации, которые вы описали, - это то, где вам нужно сделать все возможное, чтобы убедиться, что вы используете слабую связь в своем дизайне.

Вам действительно нужно провести модульное тестирование метода 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")

Имейте в виду, что вам не нужно тестировать средства ввода-вывода языка (если только вы не разрабатываете язык, что совершенно иная ситуация).

29 голосов
/ 28 июня 2013

Если вы привязаны к использованию raw_input (или любого другого конкретного источника ввода), я большой сторонник библиотеки mock . Учитывая код, который Марк Рушаков использовал в своем примере:

def say_hello():
    name = raw_input("What is your name? ")
    return "Hello " + name

Ваш тестовый код может использовать макет:

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 возвращает элементы списка по порядку. Это может сделать намного больше! Я бы порекомендовал проверить документацию.

5 голосов

Если вы можете уйти без использования внешнего процесса, сделайте это.

Однако существуют ситуации, когда это сложно, и вы действительно хотите использовать процесс, например, вы хотите проверить интерфейс командной строки исполняемого файла 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, аргументы командной строки и окружение. Кроме того, проверьте тесты для этого модуля в директории «tests». Для этого должны быть гораздо лучшие модули, поэтому возьмите мои только для учебы.

3 голосов
/ 23 апреля 2015

Использование 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'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...