如何消除pytest中loguru logger补丁的重复代码
解决Loguru日志文件占用导致的Pytest清理错误,减少重复补丁代码
问题场景
测试启动时调用loguru.logger.add("file.log")的函数时,Windows环境下会出现文件占用错误:
PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'path/to/tmp_dir/file.log'
当前临时方案是给每个测试用例添加@patch("loguru.logger.add")装饰器,但大部分测试仅需要补丁让代码正常运行,无需校验logger.add的调用,导致大量重复代码。
已尝试的无效方案
在conftest.py或测试文件中添加autouse=True的fixture尝试全局补丁,但均无效——原因是自定义caplog fixture需要调用logger.add添加日志捕获handler,全局补丁会覆盖该调用,导致caplog无法正常工作:
- 方案1:直接替换全局
logger.add为无操作函数或Mock@pytest.fixture(autouse=True) def patch_logger_add(monkeypatch): monkeypatch.setattr("loguru.logger.add", lambda *args, **kwargs: None) # 或使用MagicMock # monkeypatch.setattr("loguru.logger.add", MagicMock()) - 方案2:删除全局
logger.add属性@pytest.fixture(autouse=True) def no_logger_add(monkeypatch): monkeypatch.delattr("loguru.logger.add")
正确解决方案
核心思路是仅补丁业务代码中的logger.add调用,保留全局loguru.logger.add的功能,确保自定义caplog能正常添加捕获handler。
1. 全局自动补丁(适用于无需校验logger.add的测试)
在conftest.py中添加自动生效的fixture,针对业务模块导入的logger.add进行补丁:
import pytest from unittest.mock import patch, MagicMock from loguru import logger from _pytest.logging import LogCaptureFixture # 全局自动补丁业务代码中的logger.add,阻止创建本地日志文件 @pytest.fixture(autouse=True) def patch_business_logger_add(): # 替换业务模块中的logger.add为Mock,这里以compute_statistics为例 with patch("compute_statistics.logger.add", MagicMock()) as mock_add: yield mock_add # 保持自定义caplog fixture不变,确保能捕获日志 @pytest.fixture def caplog(caplog: LogCaptureFixture): handler_id = logger.add( caplog.handler, format="{message}", level=0, filter=lambda record: record["level"].no >= caplog.handler.level, enqueue=False, ) yield caplog logger.remove(handler_id)
2. 按需校验logger.add调用(适用于需要验证的测试)
对于需要校验logger.add调用的测试,直接注入上述fixture即可获取Mock对象,无需重复添加装饰器:
import logging import pytest from compute_statistics import ExampleClass def assert_no_errors_logged(caplog): error_messages = [record for record in caplog.records if record.levelno >= logging.ERROR] assert len(error_messages) == 0 # 普通测试:无需关注logger.add,直接运行 def test_example_class(caplog): ex = ExampleClass() result = ex.compute() assert result == "Done" assert_no_errors_logged(caplog) # 需要校验logger.add的测试:注入fixture获取Mock def test_logger_add_called(patch_business_logger_add): ex = ExampleClass() ex.compute() patch_business_logger_add.assert_called_once_with("0_example.log")
3. 多业务模块场景扩展
如果多个业务模块都调用了logger.add,可以在fixture中批量补丁:
@pytest.fixture(autouse=True) def patch_all_business_logger_add(): # 列出所有需要补丁的业务模块 modules_to_patch = [ "compute_statistics.logger.add", "another_module.logger.add", "third_module.logger.add" ] # 使用multiple_patch批量处理 with patch.multiple(*modules_to_patch, spec_set=True, return_value=None) as mocks: yield mocks
内容的提问来源于stack exchange,提问作者codeananda
相关产品推荐
相关产品推荐

