侦探行李在集成测试中不起作用

Sleuth baggage is not working in integration test

请提示我如何让 Sleuth Baggage 在集成测试中工作。 它在测试通过模拟 HTTP 请求时按预期工作,但在测试直接调用服务时不起作用。 我的应用程序是(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

测试:

@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
    }
}

这是预料之中的,因为您只是在调用控制器的方法。 Sleuth instrumentation 基本上是一个 Servlet Filter,它在第一个测试中没有被调用,但在第二个测试中被使用,因为它模拟接收一个真实的请求(而且,它并不是真正的集成测试)。

您可以create a span manually and set the baggage模拟您正在跳过的行为。

请阅读有关 Brave 中刷新和更新选项的文档 - https://docs.spring.io/spring-cloud-sleuth/docs/current/reference/html/project-features.html#features-baggage

设置这个然后重试

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

@Bean
ScopeDecorator mdcScopeDecorator() {
    return MDCScopeDecorator.newBuilder()
            .clear()
            .add(SingleCorrelationField.newBuilder(countryCodeField())
                    .flushOnUpdate()
                    .build())
            .build();
}

编辑:

在您的评论中您声明:

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

没有这样的集成,我看不出有任何意义。你必须自己开始一个跨度,在你的测试中手动并将它放在范围内。您可以在此处找到示例 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();
    }
}

如果出于任何原因开始一个跨度应该在许多测试中重复使用,那就是我的 JUnit 5 扩展:

public class SleuthExtension implements BeforeEachCallback, AfterEachCallback {

    private static final Namespace SPAN_STORE_KEY = Namespace.create(SleuthExtension.class.getName());
    private static final String SPAN_KEY = "test span";

    @Override
    public void beforeEach(ExtensionContext context) {
        Tracer tracer = SpringExtension.getApplicationContext(context)
            .getBean(Tracer.class);
        ScopedSpan span = tracer.startScopedSpan("span for " + context.getDisplayName());

        context.getStore(SPAN_STORE_KEY)
            .put(SPAN_KEY, span);
    }

    @Override
    public void afterEach(ExtensionContext context) {
        context.getStore(SPAN_STORE_KEY)
            .remove(SPAN_KEY, ScopedSpan.class)
            .end();
    }

}