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

unittest与logging:捕获StreamHandler(sys.stdout)日志时captured.records为空的问题问询

问题1:为什么captured返回空值?

核心原因是你的日志输出发生在assertLogs上下文之外。当你在测试代码里导入MyCode模块时,模块顶层的logger.error('Started')代码就已经执行了——这时候你的测试函数test_logging里的assertLogs上下文还没启动呢!assertLogs只能捕获在它的with代码块内部产生的日志,所以之前输出的日志自然不会被记录到captured中,导致captured.records是空列表。

问题2:如何捕获发送到StreamHandler(sys.stdout)的日志?

要解决这个问题,你需要调整代码结构,让日志的产生落在assertLogs的上下文范围内,同时确保日志配置和测试逻辑兼容。下面是具体的修改方案:

步骤1:重构MyCode.py,将日志逻辑封装为函数

把模块顶层的日志触发代码放到函数里,这样可以在测试时手动控制它的执行时机:

import logging
import sys

def setup_logger():
    """初始化日志配置"""
    logger = logging.getLogger()
    # 先清空已有handler,避免重复输出(可选但推荐)
    if logger.handlers:
        logger.handlers.clear()
    streamHandler = logging.StreamHandler(sys.stdout)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    streamHandler.setFormatter(formatter)
    logger.addHandler(streamHandler)
    # 设置日志级别,确保ERROR级别的日志能被捕获
    logger.setLevel(logging.ERROR)
    return logger

def log_started():
    """触发需要测试的日志"""
    logger = logging.getLogger()
    logger.error('Started')

步骤2:修改测试代码,在assertLogs上下文内触发日志

调整测试逻辑,确保日志是在assertLogs的with块里产生的:

import unittest
from unittest import TestCase
import logging
import MyCode

class TestExample(TestCase):
    def setUp(self):
        # 测试前重置root日志器,避免其他测试或模块的配置干扰
        self.root_logger = logging.getLogger()
        self.original_handlers = self.root_logger.handlers.copy()
        self.original_level = self.root_logger.level

    def tearDown(self):
        # 测试后恢复日志器的原始配置
        self.root_logger.handlers = self.original_handlers
        self.root_logger.setLevel(self.original_level)

    def test_logging(self):
        # 初始化日志器
        MyCode.setup_logger()
        # 使用assertLogs指定捕获ERROR级别及以上的日志
        with self.assertLogs(level='ERROR') as captured:
            # 在上下文内触发日志
            MyCode.log_started()
            # 断言日志记录数量
            self.assertEqual(len(captured.records), 1)
            # 断言日志内容
            self.assertTrue("Started" in captured.records[0].getMessage())

if __name__ == '__main__':
    unittest.main()

额外说明

  • assertLogs默认捕获WARNING及以上级别的日志,这里显式指定level='ERROR'是为了更清晰,也可以根据需要调整。
  • 在setUp和tearDown中重置日志器配置,是为了避免不同测试用例之间的日志配置互相干扰,保证测试的独立性。
  • 如果你的日志逻辑是业务代码的一部分,只需要确保在测试时,触发日志的代码执行在assertLogs的上下文内即可,不用完全照搬上面的结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 15:02:34