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

单元测试时如何让Mockito使用真实Log4j输出日志且无需修改原有代码

问题原因

你两个测试用例不生效的核心原因分别是:

  • 第一个用例testGetSumRealLogger:你先执行了Mockito.mockStatic(Logger.class),此时Logger的所有静态方法都被Mock接管,未打桩的方法默认返回null,你在Mock块内调用Logger.getLogger(getClass())拿到的本身就是null,注入给Utility后自然为空。
  • 第二个用例testGetSumMockedLogger:你传入的是Mockito生成的Mock Logger对象,默认所有方法都是空实现,不会触发实际的日志打印逻辑。

解决方案

不需要修改原有业务代码,根据Utility类的加载时机选择对应方案即可:

方案1:Utility类未被测试提前加载

调整获取真实Logger的顺序,先拿到真实Logger实例再Mock静态方法,避免Mock影响真实实例的获取:

@Test
void testGetSumWithRealLogger() throws Exception {
    // 先在Mock静态方法前获取到真实的Logger实例
    Logger realLogger = Logger.getLogger(Utility.class);
    // 再Mock Logger的静态方法
    try (MockedStatic<Logger> mockedStaticLogger = Mockito.mockStatic(Logger.class)) {
        // 打桩返回预先获取的真实Logger
        mockedStaticLogger.when(() -> Logger.getLogger(Utility.class)).thenReturn(realLogger);
        assertEquals(4, Utility.getSum(2, 2));
    }
}

此时Utility类首次加载初始化静态log时,会拿到你注入的真实Logger实例,走正常的log4j配置输出日志。

方案2:Utility类已被其他测试提前加载

如果Utility类在当前测试运行前已经完成类加载,静态log已经初始化完成,Mock静态方法不会再触发重新赋值,直接用反射修改静态log字段即可:

import java.lang.reflect.Field;

@Test
void testGetSumWithRealLogger() throws Exception {
    // 获取Utility类的log字段
    Field logField = Utility.class.getDeclaredField("log");
    logField.setAccessible(true);
    // 保存原有Logger实例,测试结束后回滚避免影响其他用例
    Logger originalLogger = (Logger) logField.get(null);
    try {
        // 替换为真实Logger实例
        Logger realLogger = Logger.getLogger(Utility.class);
        logField.set(null, realLogger);
        assertEquals(4, Utility.getSum(2, 2));
    } finally {
        // 恢复原有配置
        logField.set(null, originalLogger);
        logField.setAccessible(false);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 10:45:03