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

编写JUnit测试时无法创建Timer实例,触发NullPointerException

问题:Mock Micrometer MeterRegistry时Timer实例为null触发空指针异常

业务代码

public void histogram(final String metricType,  final Tags tags, final long startTime) {
    long responseTimeMillis = System.currentTimeMillis() - startTime;
    Timer t = Timer.builder(metricType)
            .publishPercentileHistogram()
            .tags(tags)
            .serviceLevelObjectives(
                    Duration.ofMillis(400),
                    Duration.ofMillis(600),
                    Duration.ofMillis(1000),
                    Duration.ofMillis(2000),
                    Duration.ofMillis(5000),
                    Duration.ofMillis(15000),
                    Duration.ofMillis(20000),
                    Duration.ofMillis(30000),
                    Duration.ofMillis(60_000),
                    Duration.ofMillis(120_000),
                    Duration.ofMillis(180_000),
                    Duration.ofMillis(240_000))
            .maximumExpectedValue(Duration.ofMillis(240_000))
            .register(this.meterRegistry);
    t.record(Duration.ofMillis(responseTimeMillis));
}

测试代码

void durationMetricHistogramTest() {
    Tags tags = Tags.of("k", "v");
    MeterRegistry.Config config = mock(MeterRegistry.Config.class);
    PauseDetector pauseDetector = mock(PauseDetector.class);
    when(meterRegistry.config()).thenReturn(config);
    when(meterRegistry.config().pauseDetector()).thenReturn(pauseDetector);
    metricsService.histogram("some_duration", tags, System.currentTimeMillis() - 1000);
}

报错信息

java.lang.NullPointerException: Cannot invoke "io.micrometer.core.instrument.Timer.record(java.time.Duration)" because "t" is null


解决方法

问题根源:你仅Mock了MeterRegistry的config()方法,但**Timer.register(MeterRegistry)内部会调用注册中心的timer()方法获取/创建Timer实例**,未对该方法配置Mock返回值,导致默认返回null。

方案1:Mock MeterRegistry的timer()方法,返回Mock Timer

直接在测试代码中补充timer()方法的Mock逻辑:

void durationMetricHistogramTest() {
    Tags tags = Tags.of("k", "v");
    // Mock Timer实例
    Timer mockTimer = mock(Timer.class);
    // 配置meterRegistry调用timer()时返回mockTimer
    when(meterRegistry.timer(any(Timer.Id.class))).thenReturn(mockTimer);
    
    // 保留原有config相关Mock(若业务代码需要)
    MeterRegistry.Config config = mock(MeterRegistry.Config.class);
    PauseDetector pauseDetector = mock(PauseDetector.class);
    when(meterRegistry.config()).thenReturn(config);
    when(meterRegistry.config().pauseDetector()).thenReturn(pauseDetector);
    
    metricsService.histogram("some_duration", tags, System.currentTimeMillis() - 1000);
    
    // 可选:验证Timer的record方法是否被正确调用
    verify(mockTimer).record(any(Duration.class));
}

方案2:使用Micrometer官方测试专用SimpleMeterRegistry

无需Mock,改用内存实现的SimpleMeterRegistry(专为测试设计),能更真实模拟Metrics行为,还可验证指标生成情况:

void durationMetricHistogramTest() {
    // 替换Mock注册中心为测试专用实现
    SimpleMeterRegistry testRegistry = new SimpleMeterRegistry();
    // 将testRegistry注入metricsService(根据自身依赖注入方式调整)
    metricsService = new MetricsService(testRegistry);
    
    Tags tags = Tags.of("k", "v");
    long startTime = System.currentTimeMillis() - 1000;
    metricsService.histogram("some_duration", tags, startTime);
    
    // 验证Timer是否正确注册并记录数据
    Timer timer = testRegistry.find("some_duration").tags("k", "v").timer();
    assertNotNull(timer);
    assertEquals(1, timer.count());
    // 验证记录时长符合预期(约1000ms)
    assertTrue(timer.totalTime(Duration.ofMillis(1)).getValue() >= 1);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 09:07:22