Companion repository for the ankurm.com article. Every transcript in docs/output/ was produced by running this project; scripts/run-all.sh regenerates all of them. Verified against Spring Boot 4.1.1 / Framework 7.0.9 / Security 7.1.1 / Micrometer 1.17.1 / kafka-clients 4.2.1 on Temurin JDK 25.0.4.1+1.
3.5 KiB
← 08 The diagnostics endpoint · 09 · Testing Actuator · README →
09 — Testing Actuator
Eight tests, and none of them assert the happy path. They pin the behaviour that would be expensive to rediscover after an upgrade.
Pin the surprises
ActuatorExposureTests runs
with exposeall,secured — the widest exposure with real security — and asserts:
@Test
void heapdumpIsNotExposedEvenWithWildcardExposure() throws Exception {
mvc().perform(get("/actuator/heapdump")).andExpect(status().isNotFound());
}
If a future Boot release changes the access default for heapdump, or if somebody adds
management.endpoint.heapdump.access=unrestricted to a shared config, this test is where you
find out. Same for shutdown.
@Test
void envMasksEveryValueRegardlessOfKeyName() throws Exception {
// acme.partner.credential matches no password/secret/token pattern.
... .andExpect(jsonPath("$.property.value").value("******"));
}
@Test
void anonymousHealthCarriesNoComponentBreakdown() throws Exception {
mvc().perform(get("/actuator/health"))
.andExpect(jsonPath("$.components").doesNotExist());
}
That last one is the show-details: when-authorized contract from
chapter 04, asserted rather than assumed. It is a one-line
configuration change away from leaking your dependency map to anonymous callers.
Test the operational contract, not the indicator
@Test
void upstreamOutageMovesTheExternalApiIndicatorButNotLiveness() {
upstream.set(UpstreamState.Mode.DOWN);
assertThat(statusOf("externalApi")).isEqualTo(Status.DOWN);
assertThat(statusOf("livenessState")).isEqualTo(Status.UP);
}
That is chapter 07 as an executable assertion. It fails if someone
adds externalApi to the liveness group, which is the exact change that causes a fleet-wide
restart loop.
@Test
void theExternalApiIndicatorRespectsItsTimeoutBudget() {
upstream.set(UpstreamState.Mode.SLOW); // the stub sleeps 30s
...
assertThat(elapsedMs).isLessThan(5_000L);
}
Fails if someone removes the read timeout. A generous bound, because timing assertions on CI are how you get flaky suites — but 5 s versus 30 s is a wide enough gap to be safe.
DEFINED_PORT, and why
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
Not the default MOCK environment. ExternalApiHealthIndicator makes a real HTTP call to this
application's own stub controller, so a servlet container has to be listening on the port it was
configured with. Under MOCK the indicator reports DOWN with a connection error and every
assertion about upstream state is meaningless — which is precisely what happened on the first
run of this suite.
RANDOM_PORT does not help here: the indicator resolves its URL at bean construction, before
the server binds, so local.server.port is not available to it.
What is not tested
There is no test for the 60-second Kafka timeout. A test that takes a minute to pass does not
belong in a suite people run before pushing —
scripts/demo-kafka-timeout.sh captures it instead, and the
transcript is committed. Some evidence is better as a script than as an assertion.