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

如何使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 14:54:02