Pytest caplog对fixture创建的自定义logger生效但测试用例内直接创建不生效
问题产生原因
核心问题和caplog fixture的执行顺序、dictConfig的默认行为有关:
caplog是pytest的函数级fixture,会在测试函数正式执行前,将自身的日志捕获handler挂载到日志体系的root logger上,用来收集所有产生的日志内容。- 你调用
logging.config.dictConfig()时,disable_existing_loggers参数默认值为True,运行时会直接清除所有已经存在的日志handler,包括刚挂载的caplog捕获handler。 - 使用
scope="module"的fixture时,模块级fixture的执行优先级远高于函数级fixture:你先完成了自定义logger的全量配置,之后caplog才挂载自己的捕获handler,不会被清除,因此测试正常通过。 - 把logger配置逻辑直接写在测试函数内部时,执行顺序变成:
caplog挂载捕获handler → 你调用dictConfig清除所有已有handler → 打印日志时已经没有caplog的handler来捕获内容,所以断言失败。
另外你使用的QueueListenerHandler是异步处理日志的,就算handler没被清除,也可能出现断言时日志还没完成队列处理、没传播到root logger的情况。
无需fixture的解决方案
方案1:修改dictConfig配置,禁止清除已有handler
在调用dictConfig时显式关闭清除已有handler的逻辑,保证caplog的捕获handler不会被删掉:
def test_logger_emits_with_queue_handler(caplog): """测试队列handler是否可以正常输出日志""" logging_config = yaml.safe_load(config_yaml) # 新增disable_existing_loggers参数,不清除已挂载的handler logging.config.dictConfig(logging_config, disable_existing_loggers=False) logger = logging.getLogger("test_logger") logger.info("This is a test") # 等待队列日志处理完成,也可以直接获取队列实例调用join()方法更优雅 import time time.sleep(0.1) assert "This is a test" in caplog.text
你也可以直接在Yaml配置的根层级添加disable_existing_loggers: false,不用修改代码里的dictConfig调用。
方案2:手动重新挂载caplog的handler
如果你必须保留dictConfig清除已有handler的默认逻辑,可以在配置完成后手动把caplog的handler加回root logger:
def test_logger_emits_with_queue_handler(caplog): logging_config = yaml.safe_load(config_yaml) logging.config.dictConfig(logging_config) # 手动把caplog的handler添加到root logger logging.getLogger().addHandler(caplog.handler) logger = logging.getLogger("test_logger") logger.info("This is a test") time.sleep(0.1) assert "This is a test" in caplog.text
内容的提问来源于stack exchange,提问作者Zobayer Hasan
相关产品推荐
相关产品推荐

