编写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
相关产品推荐
相关产品推荐

