Как перехватить вывод stdout/stderr

Поведение захвата stdout/stderr/stdin по умолчанию

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

Кроме того, stdin устанавливается на «нулевой» объект, который будет терпеть неудачу при попытке чтения из него, поскольку редко хочется ждать интерактивного ввода при выполнении автоматизированных тестов.

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

Настройка методов захвата или отключение захвата

Существует три способа, с помощью которых pytest может выполнять захват:

  • fd (файловый дескриптор) уровень перехвата (по умолчанию): Все записи, идущие в файловые дескрипторы 1 и 2 операционной системы, будут перехвачены.

  • Перехват на уровне sys: Перехватываются только записи в файлы Python sys.stdout и sys.stderr. Перехват записей в файловые дескрипторы не производится.

  • Перехват tee-sys: Записи Python в sys.stdout и sys.stderr будут перехвачены, однако записи также будут переданы в фактические sys.stdout и sys.stderr. Это позволяет «распечатывать» вывод и перехватывать его для использования в плагинах, таких как junitxml (новое в pytest 5.4).

Вы можете влиять на механизмы захвата вывода из командной строки:

pytest -s                  # disable all capturing
pytest --capture=sys       # replace sys.stdout/stderr with in-mem files
pytest --capture=fd        # also point filedescriptors 1 and 2 to temp file
pytest --capture=tee-sys   # combines 'sys' and '-s', capturing sys.stdout/stderr
                           # and passing it along to the actual sys.stdout/stderr

Использование операторов печати для отладки

Одно из главных преимуществ захвата вывода stdout/stderr по умолчанию заключается в том, что вы можете использовать операторы печати для отладки:

# content of test_module.py


def setup_function(function):
    print("setting up", function)


def test_func1():
    assert True


def test_func2():
    assert False

и запуск этого модуля покажет вам именно вывод сбойной функции и скроет другую:

$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 2 items

test_module.py .F                                                    [100%]

================================= FAILURES =================================
________________________________ test_func2 ________________________________

    def test_func2():
>       assert False
E       assert False

test_module.py:12: AssertionError
-------------------------- Captured stdout setup ---------------------------
setting up <function test_func2 at 0xdeadbeef0001>
========================= short test summary info ==========================
FAILED test_module.py::test_func2 - assert False
======================= 1 failed, 1 passed in 0.12s ========================

Доступ к захваченному выходу из тестовой функции

Фиксы capsys, capsysbinary, capfd и capfdbinary позволяют получить доступ к выводу stdout/stderr, созданному во время выполнения теста. Вот пример тестовой функции, которая выполняет некоторые проверки, связанные с выводом:

def test_myoutput(capsys):  # or use "capfd" for fd-level
    print("hello")
    sys.stderr.write("world\n")
    captured = capsys.readouterr()
    assert captured.out == "hello\n"
    assert captured.err == "world\n"
    print("next")
    captured = capsys.readouterr()
    assert captured.out == "next\n"

Вызов readouterr() делает снимок вывода на данный момент - и захват будет продолжен. После завершения работы тестовой функции исходные потоки будут восстановлены. Использование capsys таким образом освобождает ваш тест от необходимости заботиться об установке/сбросе выходных потоков, а также хорошо взаимодействует с собственным захватом pytest для каждого теста.

Если вы хотите захватить вывод на уровне файлового дескриптора, вы можете использовать приспособление capfd, которое предлагает точно такой же интерфейс, но позволяет также захватывать вывод из библиотек или подпроцессов, которые напрямую пишут в выходные потоки уровня операционной системы (FD1 и FD2).

Возвращаемое значение из readouterr изменилось на namedtuple с двумя атрибутами, out и err.

Если тестируемый код записывает нетекстовые данные, это можно зафиксировать с помощью приспособления capsysbinary, которое вместо этого возвращает bytes из метода readouterr.

Если тестируемый код записывает нетекстовые данные, вы можете зафиксировать это с помощью приспособления capfdbinary, которое вместо этого возвращает bytes из метода readouterr. Приспособление capfdbinary работает на уровне филедескриптора.

Чтобы временно отключить захват внутри теста, и capsys, и capfd имеют метод disabled(), который можно использовать в качестве менеджера контекста, отключая захват внутри блока with:

def test_disabling_capturing(capsys):
    print("this output is captured")
    with capsys.disabled():
        print("output not captured, going directly to sys.stdout")
    print("this output is also captured")
Вернуться на верх