Как перехватить вывод stdout/stderr¶
Поведение захвата stdout/stderr/stdin по умолчанию¶
Во время выполнения теста перехватывается любой вывод, отправленный на stdout
и stderr
. Если тест или метод настройки завершается неудачно, соответствующий захваченный вывод обычно показывается вместе с трассировкой ошибки. (это поведение можно настроить с помощью опции командной строки --show-capture
).
Кроме того, stdin
устанавливается на «нулевой» объект, который будет терпеть неудачу при попытке чтения из него, поскольку редко хочется ждать интерактивного ввода при выполнении автоматизированных тестов.
По умолчанию перехват осуществляется путем перехвата записей в дескрипторы файлов низкого уровня. Это позволяет перехватывать вывод из простых операторов печати, а также вывод из подпроцесса, запущенного тестом.
Настройка методов захвата или отключение захвата¶
Существует три способа, с помощью которых pytest
может выполнять захват:
fd
(файловый дескриптор) уровень перехвата (по умолчанию): Все записи, идущие в файловые дескрипторы 1 и 2 операционной системы, будут перехвачены.Перехват на уровне
sys
: Перехватываются только записи в файлы Pythonsys.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")