Сообщить об ошибке.

Управление ведением журнала (logging) в pytest

Модуль pytest автоматически фиксирует сообщения уровня WARNING и выше c отображением их в отдельном разделе для каждого неудавшегося теста так же, как захваченные stdout и stderr.

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

При желании, можно указать pytest формат ведения журнала и даты для всего, что поддерживает модуль logging, передав определенные параметры форматирования в CLI:

pytest --log-format="%(asctime)s %(levelname)s %(message)s" \
        --log-date-format="%Y-%m-%d %H:%M:%S"

Эти параметры также можно настроить с помощью файла pytest.ini:

[pytest]
log_format = %(asctime)s %(levelname)s %(message)s
log_date_format = %Y-%m-%d %H:%M:%S

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

pytest --show-capture=no

Содержание:


Встроенная фикстура caplog.

Внутри тестов можно изменить уровень логирования для захваченных сообщений журнала. Такое поведение поддерживается встроенной фикстурой caplog:

def test_foo(caplog):
    caplog.set_level(logging.INFO)
    pass

По умолчанию, уровень логирования устанавливается для корневого регистратора, однако для удобства можно установить уровень журнала для любого регистратора:

def test_foo(caplog):
    caplog.set_level(logging.CRITICAL, logger="root.baz")
    pass

Установленные уровни журналов восстанавливаются автоматически по окончании теста. Также можно использовать диспетчер контекста для временного изменения уровня журнала внутри блока with:

def test_bar(caplog):
    with caplog.at_level(logging.INFO):
        pass

По умолчанию это влияет на уровень корневого регистратора, но вместо этого можно изменить уровень любого регистратора с помощью:

def test_bar(caplog):
    with caplog.at_level(logging.CRITICAL, logger="root.baz"):
        pass

Все журналы, отправленные в регистратор во время тестового прогона, становятся доступными в фикстуре в виде как экземпляров logging.LogRecord, так и окончательного текста журнала. Такое поведение полезно, когда необходимо проверить уровень логирования:

def test_baz(caplog):
    func_under_test()
    for record in caplog.records:
        assert record.levelname != "CRITICAL"
    assert "wally" not in caplog.text

Все доступные атрибуты записей журнала представлены в описании класса logging.LogRecord.

Также можно прибегнуть к caplog.record_tuples, если например, нужно убедиться, что определенные сообщения были зарегистрированы под заданным именем регистратора с заданным уровнем логирования и сообщением:

def test_foo(caplog):
    logging.getLogger().info("boo %s", "arg")

    assert caplog.record_tuples == [("root", logging.INFO, "boo arg")]

Можно вызвать caplog.clear() для сброса захваченных записей журнала в тесте:

def test_something_with_clearing_records(caplog):
    some_method_that_creates_log_records()
    caplog.clear()
    your_test_method()
    assert ["Foo"] == [rec.message for rec in caplog.records]

Атрибут caplog.records содержит записи только текущего этапа тестирования, поэтому фазы настройки тестов будут содержать только эти журналы, то же самое с фазами вызова call и очистки ресурсов teardown.

Чтобы получить доступ к журналам с других этапов тестирования, необходимо использовать метод caplog.get_records(when). Например, для проверки того, что тесты, использующие определенную фикстуру, не регистрируют никаких предупреждений, можно проверить записи журналов для этапов настройки setup и вызова call следующим образом:

@pytest.fixture
def window(caplog):
    window = create_window()
    yield window
    for when in ("setup", "call"):
        messages = [
            x.message for x in caplog.get_records(when) if x.levelno == logging.WARNING
        ]
        if messages:
            pytest.fail(
                f"сообщения 'warning', обнаруженные во время тестирования: {messages}"
            )

Настройки встроенного ведения логов в pytest.

Для того, чтобы фреймворк pytest выводил записи журнала в том виде, в каком они выводятся непосредственно в консоль, для параметра конфигурации log_cli необходимо установить значение true.

# pytest.ini
[pytest]
log_cli=true

Можно указать уровень ведения логов, для которого записи журнала с равным или более высоким уровнем выводятся на консоль, передав опцию CLI --log-cli-level. Этот параметр принимает имена уровней ведения журнала, или целое число в качестве номера уровня логов.

Кроме того, можно указывать опции CLI --log-cli-format и --log-cli-date-format, которые отражают и по умолчанию --log-format и --log-date-format, если они не указаны, но применяются только к обработчику протоколирования консоли.

Все параметры CLI ведения логов можно установить в INI-файле конфигурации:

  • log_cli_level: устанавливает минимальный уровень сообщений журнала, которые должны быть захвачены для ведения журнала в реальном времени. Можно использовать целочисленное значение или имена уровней.

    [pytest]
    log_cli_level = INFO
    
  • log_cli_format: задает совместимую с модулем logging строку журнала, используемую для форматирования сообщений ведения журнала в реальном времени.

    [pytest]
    log_cli_level = %(asctime)s %(levelname)s %(message)s
    
  • log_cli_date_format: задает строку, совместимую с time.strftime(), которая будет использоваться при форматировании дат для записи в реальном времени.

    [pytest]
    log_cli_level = %Y-%m-%d %H:%M:%S
    

Если нужно записать все вызовы журнала тестов в файл, то можно передать опцию CLI --log-file=/path/to/log/file. Этот файл журнала открывается в режиме записи, и он будет перезаписываться при каждом сеансе выполнения тестов. Обратите внимание, что относительные пути для расположения файла журнала, независимо от того, переданы ли они через интерфейс командной строки или объявлены в файле конфигурации, всегда разрешаются относительно текущего рабочего каталога.

Для файла журнала можно указать уровень логирования, передав опцию --log-file-level. Этот параметр принимает имена уровней ведения журнала (т. е. имена уровней в верхнем регистре), или целое число в качестве номера уровня.

Кроме того, можно указать --log-file-format и --log-file-date-format, которые эквивалентны --log-format и --log-date-format, но применяются к обработчику регистрации файла журнала.

Все параметры файла журнала также можно установить в INI-файле конфигурации:

  • log_file: задает имя файла относительно текущего рабочего каталога, в который должны записываться сообщения журнала, в дополнение к другим активным средствам ведения журнала.

    [pytest]
    log_file = logs/pytest-logs.txt
    
  • log_file_level: устанавливает минимальный уровень сообщения, который должен быть захвачен для файла журнала. Можно использовать целочисленное значение или имена уровней.

    [pytest]
    log_file_level = INFO
    
  • log_file_format: задает строку, совместимую с модулем logging, используемую для форматирования сообщений, перенаправляемых в файл журнала.

    [pytest]
    log_file_format = %(asctime)s %(levelname)s %(message)s
    
  • log_file_date_format: задает строку, совместимую с time.strftime(), которая будет использоваться при форматировании дат для файла журнала.

    [pytest]
    log_file_date_format = %Y-%m-%d %H:%M:%S
    

Можно вызвать set_log_path() для динамической настройки пути log_file. Эта функция считается экспериментальной.

Класс LogCaptureFixture.

Класс LogCaptureFixture обеспечивает доступ и управление записью журнала и имеет следующие свойства и методы:

LogCaptureFixture.handler:

Свойство LogCaptureFixture.handler получает обработчик ведения журнала, используемый фикстурой. Возвращаемый тип LogCaptureHandler.

LogCaptureFixture.get_records(when):

Метод LogCaptureFixture.get_records() получает записи журнала для одного из возможных этапов тестирования..

Аргумент when - это строка с фазой тестирования. Допустимыми значениями являются: 'setup', 'call' and 'teardown'. Возвращает список захваченных записей на данном этапе (List[logging.LogRecord])

LogCaptureFixture.text:

Свойство LogCaptureFixture.text возвращает отформатированный текст журнала.

LogCaptureFixture.records:

Свойство LogCaptureFixture.records возвращает список записей журнала.

LogCaptureFixture.record_tuples:

Свойство LogCaptureFixture.record_tuples возвращает список урезанной версии записей журнала, предназначенных для использования при сравнении утверждений.

Формат кортежа: (logger_name, log_level, message).

LogCaptureFixture.messages:

Свойство LogCaptureFixture.messages возвращает список сообщений журнала с интерполяцией формата.

В отличие от LogCaptureFixture.records, которые содержат строку формата и параметры для интерполяции, все сообщения журнала в этом списке интерполируются.

В отличие от LogCaptureFixture.text, который содержит выходные данные обработчика, сообщения журнала в этом списке не содержат уровни, отметки времени и т. д., что делает точное сравнение более надежным.

_Обратите внимание), что информация о трассировке или стеке (от logging.exception() или аргументов exc_info или stack_info к функциям ведения журнала) не включается, так как она добавляется средством форматирования в обработчик.

LogCaptureFixture.clear():

Метод LogCaptureFixture.clear() сбрасывает список записей журнала и записанный текст журнала.

LogCaptureFixture.set_level(level, logger=None):

Метод LogCaptureFixture.set_level() устанавливает уровень регистратора на время теста.

  • Аргумент level - целое число, которое указывает уровень логирования.
  • Аргумент logger - строка, представляющая собой регистратор для обновления. Если не указан, то по умолчанию - корневой регистратор.

LogCaptureFixture.at_level(level, logger=None):

Метод LogCaptureFixture.at_level() представляет собой контекстный менеджер, который задает уровень для сбора журналов. После завершения инструкции with уровень восстанавливается до своего первоначального значения.

  • Аргумент level - целое число, которое указывает уровень логирования.
  • Аргумент logger - строка, представляющая собой регистратор для обновления. Если не указан, то по умолчанию - корневой регистратор.