Testing
Micrometer Observation comes with the micrometer-observation-test
module, which lets you unit-test your Observations.
Installing
It is recommended to use the BOM provided by Micrometer (or your framework if any), you can see how to configure it here. The examples below assume you are using a BOM.
Gradle
After the BOM is configured, add the following dependency:
testImplementation 'io.micrometer:micrometer-observation-test'
The version is not needed for this dependency since it is defined by the BOM. |
Maven
After the BOM is configured, add the following dependency:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-observation-test</artifactId>
<scope>test</scope>
</dependency>
The version is not needed for this dependency since it is defined by the BOM. |
Running Observation Unit Tests
Suppose you have the following production code. It will create an observation with two tags (low and high cardinality) and then call observe
to start the observation, put it in scope, close the scope, and stop the observation:
static class Example {
private final ObservationRegistry registry;
Example(ObservationRegistry registry) {
this.registry = registry;
}
void run() {
Observation.createNotStarted("foo", registry)
.lowCardinalityKeyValue("lowTag", "lowTagValue")
.highCardinalityKeyValue("highTag", "highTagValue")
.observe(() -> System.out.println("Hello"));
}
}
To unit-test this code, you can use the TestObservationRegistry
class:
@Test
void should_assert_your_observation() {
// create a test registry in your tests
TestObservationRegistry registry = TestObservationRegistry.create();
// run your production code with the TestObservationRegistry
new Example(registry).run();
// check your observation
assertThat(registry)
.doesNotHaveAnyRemainingCurrentObservation()
.hasObservationWithNameEqualTo("foo")
.that()
.hasHighCardinalityKeyValue("highTag", "highTagValue")
.hasLowCardinalityKeyValue("lowTag", "lowTagValue")
.hasBeenStarted()
.hasBeenStopped();
}
Observation Validator
If you use TestObservationRegistry
, an ObservationHandler
called ObservationValidator
is registered automatically. The purpose of this ObservationHandler
is validating the order of the calls on an Observation
(for example stop
should not be called before start
or neither of them should be called twice, etc). Please check ObservationValidatorTests
for the list of invalid scenarios.
If ObservationValidator
detects such an issue, it will throw an InvalidObservationException
which contains a validation message (explains why the Observation
is invalid), the original Observation.Context
and a history with the relevant stacktraces of the calls that were made on the Observation
. These should help you to troubleshoot what went wrong with the instrumentation. The toString()
method of the InvalidObservationException
gives you the error message and a textual summary of the history, something like this:
io.micrometer.observation.tck.InvalidObservationException: Invalid error signal: Observation has already been stopped
START: app//io.micrometer.observation.tck.ObservationValidatorTests.errorAfterStopShouldBeInvalid(ObservationValidatorTests.java:98)
STOP: app//io.micrometer.observation.tck.ObservationValidatorTests.errorAfterStopShouldBeInvalid(ObservationValidatorTests.java:99)
ERROR: app//io.micrometer.observation.tck.ObservationValidatorTests.errorAfterStopShouldBeInvalid(ObservationValidatorTests.java:100)
Based on this, it seems that error
(in ObservationValidatorTests.java
, line #100
) was called after stop
(in ObservationValidatorTests.java
, line #99
) which is an invalid scenario. If you view the error in an IDE, the locations (i.e.: ObservationValidatorTests.java:98
) should be "links" and clicking them should make the IDE jump to that line.
If you get an error like the above coming from the instrumentation of a third-party library, please open an issue/pull request for that project.