pytest capsys: как проверить stdout и stderr в тесте

pytest capsys: как проверить stdout и stderr в тесте
Содержание статьи

Если функция печатает результат, тестировать её только по возвращаемому значению недостаточно. Фикстура capsys в pytest позволяет прочитать перехваченные stdout и stderr и сравнить их с ожидаемым текстом. На Mentoring.digital разберём проверку двух потоков и типичную ошибку с повторным чтением.

Минимальный тест для двух потоков

Сохраните этот учебный пример в отдельный файл test_report.py. Он не требует сетевых запросов или доступа к рабочим данным.

import sys

def report(name):
    print(f"hello {name}")
    print("check input", file=sys.stderr)

def test_report(capsys):
    report("Ada")
    captured = capsys.readouterr()
    assert captured.out == "hello Ada\n"
    assert captured.err == "check input\n"

Запуск: python -m pytest -q test_report.py в окружении с установленным pytest. Параметр capsys передаёт pytest, вручную создавать его не нужно. В реальном проекте функцию обычно импортируют из модуля приложения.

Поле out содержит стандартный вывод, поле err вывод ошибок. Перенос строки после print() является частью результата, поэтому в ожидаемом значении стоит \n. Раздельные утверждения помогают понять, какой именно поток изменился.

Readouterr возвращает накопленное к этому моменту

def test_steps(capsys):
    print("first")
    first = capsys.readouterr()
    assert first.out == "first\n"

    print("second")
    second = capsys.readouterr()
    assert second.out == "second\n"
    assert capsys.readouterr().out == ""

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

Проверять точное равенство полезно, когда формат вывода является контрактом команды. Если проверяется только отдельное сообщение в более широком выводе, выбирайте утверждение под этот контракт. Автоматическое strip() может скрыть потерянный перенос или лишние пробелы, которые на самом деле важны.

Когда capsys нужно заменить на capfd

capsys работает на уровне потоков Python sys.stdout и sys.stderr. Для вывода через файловые дескрипторы операционной системы, в том числе от некоторых библиотек и дочерних процессов, предназначен capfd. Его результат тоже читают через readouterr().

Для двоичных данных существуют capsysbinary и capfdbinary: они возвращают байты. Если задача состоит в проверке записей стандартного logging, сначала рассмотрите caplog, чтобы проверять сами записи и уровни, а не случайное оформление консоли.

Как искать причину пустого результата

  1. Убедитесь, что тестируемая функция действительно вызвана до чтения.
  2. Проверьте, не был ли вывод уже прочитан предыдущим вызовом.
  3. Различайте Python-поток и вывод на уровне файлового дескриптора.
  4. Проверьте, завершилось ли действие, которое должно напечатать сообщение.

Начните с минимального воспроизводимого теста и затем перенесите проверку в проект. Разобрать тестирование Python-кода можно с Python-ментором. Локальная проверка примера подтверждает работу захвата, но не заменяет тестирование вашей командной утилиты.

Похожие статьи по данной теме