micrometer: Can't re-initialise TestObservationRegistry between tests

Apologies if I am missing something obvious but I’m failing to see a way to “reuse” a TestObservationRegistry across multiple tests in a test class.

Example:

@ExtendWith(SpringExtension.class)
@SpringBootTest
public class FooServiceTest {
  @Autowired
  private FooService fooService;

  @Autowired
  private TestObservationRegistry observationRegistry;

  @TestConfiguration
  static class ObservationConfiguration {
    @Bean
    TestObservationRegistry observationRegistry() {
      return TestObservationRegistry.create();
    }
  }

  @Test
  void testAddFooWithABar() {
    fooService.addWithBar();
    TestObservationRegistryAssert.assertThat(observationRegistry)
        .hasObservationWithNameEqualTo("foo.create")
        .that()
        .hasLowCardinalityKeyValue("isBar", "true");
  }

  @Test
  void testAddFooWithoutABar() {
    fooService.addWithoutBar();
    TestObservationRegistryAssert.assertThat(observationRegistry)
        .hasObservationWithNameEqualTo("foo.create")
        .that()
        .hasLowCardinalityKeyValue("isBar", "false");
  }
}

In the above, both tests call methods of the FooService which create similar Observations but with a slightly different KeyValue isBar.

When I run the test, one of the tests always fails because the hasObservationWithNameEqualTo() assertion gets the latest Observation matching the name foo.create, and the subsequent assertion on key value fails.

What is needed (I think) is some way of “clearing”/re-initialising the TestObservationRegistry between tests, but I can’t see any way of doing this short of annotating each test method with @DirtiesContext, which seems like a sledgehammer to crack a nut, and slows down test runs.

Or am I going about this the wrong way?

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 17 (9 by maintainers)

Commits related to this issue

Most upvoted comments

Don’t be so hard on yourself 😉 That’s a typical problem when running tests against a shared state. Either you lock on the state and ensure that for each of the tests from the beginning till the end you have control of it which leads of course to lower performance, or you ignore the fact that you have other tests injecting entries to your shared state but you create test data in such a way that you can later properly query them.

Example: you create a user with a name that is e.g. timestamp or name of your test + timestamp and then when you inject it to the in memory database you test that that concrete entry is there in the database. You don’t check that the database size == 1 cause in the meantime some other test could have entered something to your db.

I think that in a typical unit test setup, the TestObservationRegistry, as a test class field, will be newly instantiated for each test. This is only a problem with integration tests and an actual application context where a single registry instance is expected for the running server.

In this case, adding a clear() method could help if it’s used with @BeforeEach to ensure that the registry is empty. If developers enable parallel test execution this becomes a problem again and they would need to worry about shared state and probably use @ResourceLock. Since the application context is itself cached by the test context framework, the context (and the TestObservationRegistry bean) can be reused by another test class and side effects can also happen there.

In short, I’m not sure this really solves the problem as by definition the registry is stateful within a live application.