You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用pytest和unittest测试Python特定日志格式

解决pytest测试中验证自定义日志格式的问题

问题核心是pytest会默认替换日志配置,且assertLogs捕获的是经过pytest格式化后的日志输出,而非应用本身的格式结果。要验证自定义格式,需直接捕获原始日志记录,再用我们的格式器重新格式化后校验。

解决方案步骤

  • 避开pytest的日志处理器,直接获取日志的原始LogRecord对象
  • 使用我们定义的格式器对原始记录进行格式化
  • 校验格式化后的字符串是否符合预期格式

修改后的测试代码

替换原测试部分为以下代码:

import unittest
import logging
from logging import LogRecord

# 复用之前定义的日志格式
LOG_CONFIG_FORMAT = '%(asctime)s {%(process)d-%(thread)d} %(levelname)s \
%(name)s@%(lineno)d my_field_1=%(my_field_1)s my_field_2=%(my_field_2)s my_field_3=\
%(my_field_3)s: %(message)s '

class TestCase(unittest.TestCase):
    def test_log_format(self):
        # 创建格式器,使用我们的自定义格式
        formatter = logging.Formatter(LOG_CONFIG_FORMAT)
        captured_records = []

        # 定义一个自定义处理器,用于捕获原始LogRecord
        class CaptureHandler(logging.Handler):
            def emit(self, record: LogRecord) -> None:
                captured_records.append(record)

        # 获取应用的logger,添加自定义处理器
        app_logger = logging.getLogger(__name__)
        capture_handler = CaptureHandler()
        app_logger.addHandler(capture_handler)

        try:
            # 执行被测函数
            my_func()

            # 对每个捕获的记录进行格式化,然后校验
            for record in captured_records:
                # LoggerAdapter的extra会合并到record的__dict__中,可直接格式化
                formatted_log = formatter.format(record)
                # 校验格式中的必填字段是否存在
                self.assertIn('my_field_1=', formatted_log)
                self.assertIn('my_field_2=', formatted_log)
                self.assertIn('my_field_3=', formatted_log)
                # 校验特定场景下的字段值(比如i=2时的字段)
                if record.message == '2':
                    self.assertIn('my_field_1=BUST!!!', formatted_log)
                    self.assertIn('my_field_2=BOOM!!!', formatted_log)
                    self.assertIn('my_field_3=CRASH!!!', formatted_log)
        finally:
            # 移除处理器,避免影响其他测试
            app_logger.removeHandler(capture_handler)

关键说明

  1. 捕获原始记录:通过自定义CaptureHandler直接获取LogRecord,这是日志未被任何格式器处理的原始数据,包含所有我们设置的extra字段。
  2. 手动格式化:用我们自己的Formatter对记录进行格式化,确保得到的是应用实际会输出的格式字符串。
  3. 清理资源:测试完成后移除自定义处理器,防止对其他测试用例造成干扰。

额外优化(可选)

如果不想在每个测试中重复编写处理器逻辑,可以把捕获日志的逻辑封装成一个上下文管理器:

from contextlib import contextmanager

@contextmanager
def capture_logs(logger_name):
    logger = logging.getLogger(logger_name)
    captured = []
    handler = logging.Handler()
    handler.emit = lambda r: captured.append(r)
    logger.addHandler(handler)
    try:
        yield captured
    finally:
        logger.removeHandler(handler)

然后在测试中简化使用:

def test_log_format(self):
    formatter = logging.Formatter(LOG_CONFIG_FORMAT)
    with capture_logs(__name__) as captured_records:
        my_func()
        for record in captured_records:
            formatted = formatter.format(record)
            # 执行校验逻辑...

内容的提问来源于stack exchange,提问作者cyau

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 16:15:32