Testing Micrometer Metrics in Spring Boot Applications
Micrometer is a metrics collection library that provides a vendor-neutral API for instrumenting applications. In a Spring Boot environment, it serves as the underlying abstraction for monitoring systems such as Prometheus, Datadog, or New Relic.
While we often focus on exposing metrics to monitoring systems, it’s equally important to ensure that these metrics behave as expected, especially when they track critical events like API requests, business transactions, or errors. In this article, we’ll explore how to unit test Micrometer metrics in a Spring Boot application.
1. Project Setup
Let’s start with a simple Spring Boot application that uses Micrometer.
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<version>1.15.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
This minimal setup includes the micrometer-core library for collecting metrics and the spring-boot-starter-test for testing support.
2. Creating a Metric-Enabled Service
Let’s define a simple service that counts user logins using Micrometer’s Counter.
@Service
public class LoginService {
private final Counter loginCounter;
private final Timer loginTimer;
public LoginService(MeterRegistry meterRegistry) {
this.loginCounter = Counter.builder("app.user.logins")
.description("Number of successful user logins")
.register(meterRegistry);
this.loginTimer = Timer.builder("app.user.login.time")
.description("Time taken to process user login")
.register(meterRegistry);
}
public void login(String username) {
// simulate login logic that takes some time
loginTimer.record(() -> {
try {
Thread.sleep(100); // simulate a short operation
loginCounter.increment();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
public double getLoginCount() {
return loginCounter.count();
}
}
This LoginService uses a Counter to track the number of successful logins and a Timer to measure how long each login operation takes. Both metrics are registered with the injected MeterRegistry, which manages and records all metric data. The login() method simulates a user login by introducing a brief delay and incrementing the counter within the timer’s record() block.
3. Unit Testing with SimpleMeterRegistry
For unit testing, we don’t need the full Spring context. Instead, we can use SimpleMeterRegistry, a lightweight in-memory registry designed for testing.
public class LoginServiceTest {
@Test
void shouldIncrementLoginCounter() {
SimpleMeterRegistry meterRegistry = new SimpleMeterRegistry();
LoginService loginService = new LoginService(meterRegistry);
loginService.login("thomas");
loginService.login("pink");
double count = meterRegistry
.find("app.user.logins")
.counter()
.count();
assertThat(count).isEqualTo(2.0);
}
@Test
void shouldRecordLoginTime() {
SimpleMeterRegistry registry = new SimpleMeterRegistry();
LoginService service = new LoginService(registry);
service.login("charlie");
var timer = registry.find("app.user.login.time").timer();
double totalTime = timer.totalTime(java.util.concurrent.TimeUnit.MILLISECONDS);
assertThat(totalTime).isGreaterThan(0);
}
}
The SimpleMeterRegistry serves as a lightweight stand-in for the production registry, allowing metrics to be tested without loading the Spring context. In this setup, we instantiate the registry directly and inject it into the service. The first test verifies that the login counter increments correctly, while the second ensures that execution time is recorded in the timer.
4. Integration Testing with @SpringBootTest
For integration testing, we can rely on Spring Boot’s built-in Micrometer configuration. Spring Boot automatically injects a shared MeterRegistry into beans. However, since this registry persists across tests, we should reset it between test methods to ensure metrics from one test don’t affect another.
@SpringBootTest(classes = MicrometerTestingApplication.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
class LoginServiceIntegrationTest {
@Autowired
private MeterRegistry meterRegistry;
@Autowired
private LoginService loginService;
@Test
void shouldIncrementCounterInSpringContext() {
loginService.login("charlie");
loginService.login("david");
loginService.login("eve");
double count = meterRegistry
.find("app.user.logins")
.counter()
.count();
assertThat(count).isEqualTo(3.0);
}
@Test
void shouldRecordLoginTimeInSpringContext() {
loginService.login("frank");
var timer = meterRegistry.find("app.user.login.time").timer();
double totalTime = timer.totalTime(java.util.concurrent.TimeUnit.MILLISECONDS);
assertThat(totalTime).isGreaterThan(0);
}
@Test
void shouldHaveZeroCountAfterReset() {
double count = meterRegistry
.find("app.user.logins")
.counter()
.count();
assertThat(count).isZero();
}
}
In this integration test, we use @SpringBootTest to load the full Spring application context, allowing us to verify that Micrometer metrics are correctly configured and registered within a running Spring environment. The annotation @DirtiesContext ensures that after each test method, the Spring context is refreshed, effectively resetting the MeterRegistry and avoiding test interference due to residual metric data.
The first test calls the login() method three times and checks that the app.user.logins counter reflects this count accurately. This confirms that the counter metric is properly registered and incremented within the Spring-managed registry.
The second test validates the timer metric by confirming that the total recorded time is greater than zero after invoking the login() method. This demonstrates that the Timer successfully records execution duration when the service logic runs inside the Spring context. Finally, the third test confirms that metrics are reset between tests, ensuring that each test starts in a clean state.
5. Conclusion
In this article, we explored how to test Micrometer metrics in both isolated and integrated Spring Boot environments. We began by using SimpleMeterRegistry for lightweight unit testing, ensuring metric logic functions independently of the Spring context. Then, we moved to integration testing with @SpringBootTest to validate that metrics like counters and timers behave correctly within a real application setup.
6. Download the Source Code
This article discussed testing Micrometer metrics in a Spring Boot application.
You can download the full source code of this example here: Sign up

