单元测试时如何让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
相关产品推荐
相关产品推荐

