【问题标题】:Sleuth baggage is not working in integration test侦探行李在集成测试中不起作用
【发布时间】:2021-11-03 21:41:17
【问题描述】:

请提示我如何让 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();
        }
    }
}

应用程序.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
    }
}

【问题讨论】:

    标签: spring-boot-test spring-cloud-sleuth


    【解决方案1】:

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

    您可以create a span manuallyset the baggage 来模拟您正在跳过的行为。

    【讨论】:

    • 好吧,Sleuth 工具是一个 Servlet 过滤器,但另外还有许多其他工具,例如消息侦听器、石英调度器等。我只是想也应该有用于集成测试的工具
    • 您可以使用 JUnit @BeforeEach/@AfterEach 来启动/停止跨度,但我建议使用 MockMVC 来测试它。我也很好奇你想要真正实现什么(我是 Spring Cloud Sleuth 维护者,对你的用例很好奇)。
    • 确实,在这个简单的演示案例中,我可以使用 MockMVC 测试我的功能。但通常 MockMVC 测试的可读性不如服务方法测试。我喜欢进行一项 MockMVC 测试来验证微服务是否正常工作,并进行许多服务方法测试来验证所有情况。但我也有服务方法调度程序方法 - 侦探仪器调度程序(石英)执行器,但在测试中我调用服务方法。没错,我可以在 @BeforeEach/@AfterEach 中手动管理跨度,但我希望 JUnit Extension 可以自动实现 - 现在看来我必须自己实现它。
    【解决方案2】:

    请阅读 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();
    }
    

    编辑:

    在你的评论中你说:

    嗯,Sleuth 工具是一个 Servlet 过滤器,但另外还有许多其他工具,例如消息侦听器、石英调度器等。我只是想也应该有用于集成测试的工具

    没有这样的集成,我认为拥有一个没有任何意义。您必须自己开始一个跨度,在您的测试中手动并将其放入范围内。你可以在这里找到一个例子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();
        }
    }
    

    【讨论】:

    • Marcin,它没有帮助。您参考文档如何自动将更新后的行李传递给 MDC。但是我的问题是,当我直接调用服务方法时,我的集成测试中没有跟踪上下文,因此我的行李实际上是/dev/null
    • 现在很好,但我认为有更方便的解决方案。实际上,我从本土跟踪迁移到 Sleuth,我正在寻找一种解决方案,将集成测试的影响降至最低
    【解决方案3】:

    如果出于任何原因开始跨度应该在许多测试中重用,那就是我的 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();
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 2023-04-06
      • 1970-01-01
      • 2020-10-18
      • 1970-01-01
      • 2017-04-15
      • 2019-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多