批量运行测试时Mock无交互报错,单测正常的技术求助
问题描述
批量运行测试用例时出现以下错误,但单独运行每个测试用例无异常:
Wanted but not invoked:
logger.error("Error message 2");
-> at acme.logger.MyCoreLogTest.errorWith(MyCoreLogTest.java:74)
Actually, there were zero interactions with this mock.
相关代码
MyCoreLog类
package acme.mycore.logger; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class MyCoreLog { private static final Logger logger = LogManager.getLogger("MyCoreLog"); public static final String id = "my_id"; public static void error(String message) { if (System.getenv(id) != null) logger.error(String.format("%s, %s=%s", message, id, System.getenv(id))); else logger.error(message); } }
CoreLogTest测试类
package acme.mycore.logger; import org.apache.logging.log4j.LogManager; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.apache.logging.log4j.Logger; import org.mockito.MockedStatic; import static org.mockito.Mockito.*; class CoreLogTest { MockedStatic<LogManager> logManager; @BeforeEach public void setUp() { logManager = mockStatic(LogManager.class); } @AfterEach public void tearDown() throws Exception { logManager.close(); } @Test void error() { String errorMessage = "error message"; Logger logger = mock(Logger.class); logManager.when(() -> LogManager.getLogger("MyCoreLog")).thenReturn(logger); MyCoreLog.error(errorMessage); verify(logger).error(errorMessage); // 执行正常 } @Test void errorWith() { String errorMessage = "error message 2"; Logger logger = mock(Logger.class); logManager.when(() -> LogManager.getLogger("MyCoreLog")).thenReturn(logger); MyCoreLog.error(errorMessage); verify(logger).error(errorMessage); // 报错位置 } }
问题原因
- 静态变量类加载复用:
MyCoreLog中的logger是static final修饰的静态变量,会在类第一次被加载时初始化。批量运行测试时,第一个测试error()执行时MyCoreLog类被加载,此时通过mockStatic(LogManager)生成的mock logger被赋值给静态变量;第二个测试errorWith()执行时,MyCoreLog类已加载完成,静态logger不会重新初始化,导致第二个测试中设置的新mock logger从未被使用,verify校验的是未被调用的新mock,因此报错。 - 测试逻辑错误:
errorWith()测试的校验逻辑本身有误——当环境变量my_id存在时,MyCoreLog.error()会调用logger.error()输出格式化后的消息,而非原始的errorMessage,这也是问题暴露的潜在因素。
解决方案
推荐重构MyCoreLog类,避免静态依赖的强耦合,同时修正测试逻辑:
步骤1:修改MyCoreLog类
添加静态方法用于测试时替换logger:
package acme.mycore.logger; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class MyCoreLog { private static Logger logger = LogManager.getLogger("MyCoreLog"); public static final String id = "my_id"; // 测试专用:替换logger实例 static void setLogger(Logger customLogger) { logger = customLogger; } public static void error(String message) { if (System.getenv(id) != null) logger.error(String.format("%s, %s=%s", message, id, System.getenv(id))); else logger.error(message); } }
步骤2:修改测试类
移除静态LogManager的mock,直接替换MyCoreLog的logger,并修正测试逻辑:
package acme.mycore.logger; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.mockito.Mockito.*; class CoreLogTest { private Logger mockLogger; @BeforeEach public void setUp() { mockLogger = mock(Logger.class); MyCoreLog.setLogger(mockLogger); // 清除环境变量,避免测试间干扰 System.clearProperty(MyCoreLog.id); } @AfterEach public void tearDown() { // 恢复原始logger,避免影响其他测试 MyCoreLog.setLogger(LogManager.getLogger("MyCoreLog")); } @Test void error() { String errorMessage = "error message"; MyCoreLog.error(errorMessage); verify(mockLogger).error(errorMessage); } @Test void errorWith() { String errorMessage = "error message 2"; // 设置环境变量,测试格式化分支 System.setProperty(MyCoreLog.id, "test_value"); MyCoreLog.error(errorMessage); String expectedMessage = String.format("%s, %s=%s", errorMessage, MyCoreLog.id, "test_value"); verify(mockLogger).error(expectedMessage); } }
内容的提问来源于stack exchange,提问作者Gabriel Muñumel
相关产品推荐
相关产品推荐

