Search code examples
spring-boot-testspring-cloud-sleuth

Sleuth baggage is not working in integration test


Please hint me how to get Sleuth Baggage working in integration test. It works as expected when test pass mock HTTP request but does not work when test calls service directly. My application is (Spring Boot 2.5.4, Spring Cloud 2020.0.3):

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }

    @Bean
    BaggageField myBaggageField() {
        return BaggageField.create("my-field-name");
    }

    @RestController
    static class MyController {

        @AutowiredMyService myService;

        @GetMapping
        String callService() {
            return myService.updateAndGet();
        }
    }

    @Service
    static class MyService {

        @Autowired BaggageField baggageField;

        public String updateAndGet() {
            baggageField.updateValue("value");
            return baggageField.getValue();
        }
    }
}

application.yml: spring.sleuth.baggage.remote-fields: my-field-name

test:

@SpringBootTest
@AutoConfigureMockMvc
class MyApplicationTests {

    @Autowired
    MyService myService;

    @Autowired
    MockMvc mockMvc;

    @Test
    void testBaggageByService() {
        assertThat(myService.updateAndGet()).isEqualTo("value");         // FAILS
    }

    @Test
    void testBaggageByController() throws Exception {
        mockMvc.perform(get("/")).andExpect(content().string("value"));  // PASS
    }
}

Solution

  • Please read the docs about flush and update option in Brave - https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/html/project-features.html#features-baggage

    Set this up and try again

    // configuration
    @Bean
    BaggageField countryCodeField() {
        return BaggageField.create("my-field-name");
    }
    
    @Bean
    ScopeDecorator mdcScopeDecorator() {
        return MDCScopeDecorator.newBuilder()
                .clear()
                .add(SingleCorrelationField.newBuilder(countryCodeField())
                        .flushOnUpdate()
                        .build())
                .build();
    }
    

    EDIT:

    In your comment you state:

    Well, Sleuth instrumentation is a Servlet Filter, but additionaly there are many other instrumentations, for example for message listener, for quartz scheduler etc. I simply guess there should be also instrumentation for integration test as well

    There is no such integration and I don't see any point in having one. You have to start a span yourself, manually in your test and put it in scope. You can find an example here https://github.com/spring-cloud/spring-cloud-sleuth/blob/v3.0.3/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/CircuitBreakerIntegrationTests.java#L55-L71

    @Autowired
    Tracer tracer;
    
    @Autowired
    CircuitBreakerFactory factory;
    
    @Test
    public void should_pass_tracing_information_when_using_circuit_breaker() {
        // given
        Tracer tracer = this.tracer;
        ScopedSpan scopedSpan = null;
        try {
            scopedSpan = tracer.startScopedSpan("start");
            // when
            Span span = this.factory.create("name").run(tracer::currentSpan);
    
            BDDAssertions.then(span).isNotNull();
            BDDAssertions.then(scopedSpan.context().traceId()).isEqualTo(span.context().traceId());
        }
        finally {
            scopedSpan.end();
        }
    }