pytest 有没有办法检查日志条目是否处于错误级别或更高级别?

sil*_*cer 5 python pytest python-logging

Python 3.8.0,pytest 5.3.2,日志记录 0.5.1.2。

我的代码有一个输入循环,为了防止程序完全崩溃,我捕获任何抛出的异常,将它们记录为关键,重置程序状态,然后继续运行。这意味着导致这种异常的测试不会完全失败,只要输出仍然是预期的。如果错误是测试代码的副作用但不影响主要测试逻辑,则可能会发生这种情况。但是,我仍然想知道该测试暴露了一个导致错误的错误。

我所做的大部分谷歌搜索都显示了如何在 pytest 中显示日志的结果,我正在这样做,但我不知道是否有办法在测试中公开日志,这样我就可以在任何测试中失败错误或严重级别的日志。

编辑:这是失败尝试的最小示例:

test.py

import subject
import logging
import pytest

@pytest.fixture(autouse=True)
def no_log_errors(caplog):
    yield  # Run in teardown
    print(caplog.records)
    # caplog.set_level(logging.INFO)
    errors = [record for record in caplog.records if record.levelno >= logging.ERROR]
    assert not errors

def test_main():
    subject.main()
    # assert False
Run Code Online (Sandbox Code Playgroud)

subject.py

import logging
logger = logging.Logger('s')
def main():
    logger.critical("log critical")
Run Code Online (Sandbox Code Playgroud)

运行python3 -m pytest test.py通过没有错误。取消对 assert 语句的注释使测试失败且没有错误,并打印[]到 stdout 和log criticalstderr。

编辑2:

我找到了为什么会失败。从关于 caplog 的文档:

caplog.records 属性只包含当前阶段的记录,所以在设置阶段它只包含设置日志,与调用和拆卸阶段相同

然而,就在下面是我应该第一次发现的:

要从其他阶段访问日志,请使用 caplog.get_records(when) 方法。例如,如果您想确保使用某个夹具的测试永远不会记录任何警告,您可以在拆卸期间检查设置和调用阶段的记录,如下所示:

@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(
                "warning messages encountered during testing: {}".format(messages)
            )

Run Code Online (Sandbox Code Playgroud)

但是,这仍然没有任何区别,并且print(caplog.get_records("call"))仍然返回[]

Ant*_*ile 9

您可以使用夹具构建这样的东西caplog

这是文档中的一些示例代码,它根据级别进行一些断言:

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

由于records 是标准日志记录类型,因此您可以使用任何需要的类型

这是您可以使用autouse夹具更自动地执行此操作的一种方法:

@pytest.fixture(autouse=True)
def no_logs_gte_error(caplog):
    yield
    errors = [record for record in caplog.get_records('call') if record.levelno >= logging.ERROR]
    assert not errors
Run Code Online (Sandbox Code Playgroud)

(免责声明:我是 pytest 的核心开发人员)


Lag*_*aer 0

您可以使用该unittest.mock模块(即使使用 pytest)并对用于日志记录的任何函数/方法进行猴子补丁。然后,在您的测试中,您可以有一些断言,如果logging.error被调用,则该断言会失败。

这将是一个短期解决方案。但也可能是这样的情况,您的设计可以从更多的分离中受益,这样您就可以轻松地测试您的应用程序,而无需热心的try ... except块捕获/抑制几乎所有内容。