How to test traceId propagation with WebTestClient + Micrometer Tracer?
Asked Answered
S

2

5

After migration to Spring Boot 3 and refactor from Sleuth to Micrometer, I am no more able to test traceId propagation with WebTestClient + Tracer.

My test here, is to have a simple @RestController where I expect the traceId being propagated through the Tracer :

@RestController
class MyTraceIdController(@Autowired private val tracer: Tracer) {


    @GetMapping("/trace")
    fun info(): ResponseEntity<String> {

        val traceKey = "x-b3-traceid"

        // Using tracer the traceId is retrieved at runtime but not during test
        val responseHeaders = HttpHeaders()
        responseHeaders.set(traceKey, tracer.currentSpan()?.context()?.traceId().toString())

        return ResponseEntity
            .ok()
            .headers(responseHeaders)
            .body("OK")
    }

}

And the goal is to test it thanks to the WebTestClient :

@AutoConfigureWebTestClient
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
class MyTraceIdTests(@Autowired private val webTestClient: WebTestClient) {

    @Test
    fun `Assert info endpoint works as expected with webTestClient`() {

        val traceKey = "x-b3-traceid"
        val traceValue = "463ac35c9f6413ad48485a3953bb6124"

        val spanKey = "x-b3-spanid"
        val spanValue = "a2fb4a1d1a96d312"

        webTestClient.get()
            .uri("http://localhost:9080/trace")
            .header(traceKey, traceValue)
            .header(spanKey, spanValue)
            .exchange()
            .expectStatus().isOk
            .expectHeader().valueEquals(traceKey, traceValue)
            .expectBody<String>().isEqualTo("OK")
    }

}

With Sleuth, when running tests, the BraveTracer that was injected did perfectly the job to propagate the traceId.

With Micrometer, I am not able anymore to have one correct tracer injected to do the job.

To test the case, I have created the following sample :

https://github.com/bvoglevette/trace-id-sample

Two behaviors are observable :

  • At runtime, executing bootRun + curl on the endpoint, the BraveAutoconfiguration is used and one correct BraveTracer is injected => It works as expected
  • At test time, I am not able to get one correct BraveTracer bean injected => The traceId is always null

The different approaches I have tried without success

  • To use SimpleTracer()
  • To implement my own Tracer bean, following Micrometer documentation
  • To use the BraveAutoconfiguration in my test or to instanciate beans based on it

None of these implementations have been able to produce one bean where the traceId is propagated.

I expect more to find a way to simply inject a working bean coming from BraveAutoconfiguration. What Am I missing ? Should I do my test diffently ?

Sirup answered 1/12, 2022 at 16:25 Comment(0)
B
13

You need to add @AutoConfigureObservability on your test class. By default observability is disabled in tests.

Bayonne answered 2/1, 2023 at 12:48 Comment(1)
This doesn't configure the WebTestClient to propagate trace data automatically. Infact looking at the WebTestClient interface it doesn't support observations out of the box, anyways.Sewer
L
2

I was also migrating my spring-web and spring-integration app from spring boot 2 sleuth to spring boot 3 with micrometer and also run into issues. I wanted to configure the tracing manually and also I did not have access to actuator in all modules).

To configure the tracing manually, except for ordinary micrometer dependencies, I needed to add these two: micrometer-tracing-test and micrometer-tracing-integration-test (note: in most cases you will probably just need micrometer-tracing-test):

        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-tracing-bom</artifactId>
            <version>${micrometer.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>context-propagation</artifactId>
            <version>${micrometer.version}</version>
        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-tracing-test</artifactId>
            <version>${micrometer.version}</version>
            <scope>test</scope> (!)
            <exclusions>
                <exclusion>
                    <groupId>com.github.tomakehurst</groupId>
                    <artifactId>wiremock-jre8-standalone</artifactId>
                </exclusion>
            </exclusions>

        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-tracing-integration-test</artifactId>
            <version>${micrometer.version}</version>
            <scope>test</scope> (!)
            <exclusions>
                <exclusion>
                    <groupId>com.github.tomakehurst</groupId>
                    <artifactId>wiremock-jre8-standalone</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                </exclusion>
            </exclusions>
        </dependency>     

After that I provided the SimpleTracer bean to test context (which is Tracer implementation meant for tests) and manually set up observation registry (TestObservationRegistry). I configured the registry with observation handler - DefaultTracingObservationHandler which delagates to this test SimpleTracer:

  @Bean
  SimpleTracer simpleTracer() {
    return new SimpleTracer();
  }

  @Bean
  TestObservationRegistry observationRegistry(SimpleTracer tracer) {
    final TestObservationRegistry testObservationRegistry = TestObservationRegistry.create();
    testObservationRegistry.observationConfig().observationHandler(new DefaultTracingObservationHandler(tracer));
    
    return testObservationRegistry;
  }

NOTE: If in your test, you are getting the exception like below, than you have not configured the observation handler i.e your observation registry does not contain tracing observation handler. In such case try adding the observation handler manually (see above example).

java.lang.IllegalArgumentException: Context does not have an entry for key [class io.micrometer.tracing.handler.TracingObservationHandler$TracingContext]

    at io.micrometer.observation.Observation$Context.getRequired(Observation.java:928)
    at [email protected]/io.micrometer.tracing.handler.TracingAwareMeterObservationHandler.onStop(TracingAwareMeterObservationHandler.java:79)
    at io.micrometer.observation.ObservationHandler$FirstMatchingCompositeObservationHandler.lambda$onStop$7(ObservationHandler.java:173) 
Luing answered 6/3, 2023 at 10:9 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.