如何使用Mockito Mock带有static修饰的自定义StandardLogger?
问题根因
你的测试代码存在多处不符合Mockito使用规范的问题,直接导致logger为空、Mock不生效:
- 未在测试类上添加Mockito扩展/运行器注解,
@Mock注解未被触发解析,测试类里声明的logger对象本身就是null @InjectMocks仅能为类的实例成员变量注入Mock依赖,无法作用于ConfigurationProvider中用static final修饰的静态常量logger- 你调用的
getHostOrDefault是静态方法,直接通过类名调用,和测试类里声明的ConfigurationProvider实例无任何关联,自然不会用到你测试类中Mock的logger对象 - 用
@Mock注解标注logger的同时还手动new StandardLogger("CATS"),会覆盖Mockito生成的Mock对象,属于错误用法
解决方案
你可以根据场景选择以下任意一种方案解决问题:
方案1:使用反射修改静态变量(无需修改生产代码)
适合无法改动生产代码的场景,需要用反射强制修改ConfigurationProvider的静态final logger字段为你Mock的对象:
// JUnit5 示例 @ExtendWith(MockitoExtension.class) class ConfigurationProviderTest { @Mock private StandardLogger logger; private StandardLogger originLogger; @BeforeEach void setUp() throws NoSuchFieldException, IllegalAccessException { // 备份原有logger,测试结束后恢复避免影响其他用例 Field loggerField = ConfigurationProvider.class.getDeclaredField("logger"); loggerField.setAccessible(true); Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(loggerField, loggerField.getModifiers() & ~Modifier.FINAL); originLogger = (StandardLogger) loggerField.get(null); // 替换为Mock的logger loggerField.set(null, logger); } @Test void thatGetHostOrDefaultReturnsHost() { String hostOrDefault = ConfigurationProvider.getHostOrDefault("http://abc.host:9433", "host"); assertThat(hostOrDefault, is("abc.host")); } @AfterEach void tearDown() throws NoSuchFieldException, IllegalAccessException { // 恢复原有logger Field loggerField = ConfigurationProvider.class.getDeclaredField("logger"); loggerField.setAccessible(true); loggerField.set(null, originLogger); } }
方案2:优化生产代码可测性(更推荐)
调整生产代码设计,规避静态方法依赖静态变量的不易测问题,改造成实例依赖注入的形式:
调整后的生产代码
@Context public class ConfigurationProvider { private final StandardLogger logger; // 供框架初始化使用的无参构造 public ConfigurationProvider() { this.logger = new StandardLogger("CATS"); } // 供单元测试注入Mock的构造方法 public ConfigurationProvider(StandardLogger logger) { this.logger = logger; } // 改为实例方法 public String getHostOrDefault(String url, String defaultHost) { try { return new URL(url).getHost(); } catch (MalformedURLException e) { logger.error("ConfigurationProvider", "getHostOrDefault", "Exception returning default host : "+ defaultHost + " Exception : "+e); } return defaultHost; } }
调整后的测试代码
@ExtendWith(MockitoExtension.class) class ConfigurationProviderTest { @InjectMocks ConfigurationProvider configurationProvider; @Mock private StandardLogger logger; @Test void thatGetHostOrDefaultReturnsHost() { String hostOrDefault = configurationProvider.getHostOrDefault("http://abc.host:9433", "host"); assertThat(hostOrDefault, is("abc.host")); } // 可额外扩展异常场景验证 @Test void thatGetHostOrDefaultReturnsDefaultWhenUrlInvalid() { String hostOrDefault = configurationProvider.getHostOrDefault("invalid_url", "default.host"); assertThat(hostOrDefault, is("default.host")); // 验证logger.error方法被正常调用 Mockito.verify(logger, Mockito.times(1)).error(Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); } }
内容的提问来源于stack exchange,提问作者Ayub Ahmed
相关产品推荐
相关产品推荐

