如何在 spring-cloud-gateway 合约测试中从 spring-cloud-contract 中设置带有 StubRunner 端口的 url

Posted

技术标签:

【中文标题】如何在 spring-cloud-gateway 合约测试中从 spring-cloud-contract 中设置带有 StubRunner 端口的 url【英文标题】:How to set urls with port of StubRunner from spring-cloud-contract in spring-cloud-gateway contract tests 【发布时间】:2022-01-23 05:40:15 【问题描述】:

我有一个 spring 云网关应用程序,它将请求路由到另一个服务。另一个服务定义了合约,这些合约在测试中由 Spring Cloud Gateway 应用程序作为存根导入。

现在我想在我的网关中进行合同测试,这将消耗另一个服务的存根。问题是我不知道如何将StubRunnerPort 作为属性/环境注入,以便我的配置类可以选择它并相应地配置路由:

Api网关路由配置

@Configuration
class GatewayConfig 

    
    @Value("$subscriptions.url")
    private String subscriptionsUrl;

    @Autowired
    private TokenRelayGatewayFilterFactory tokenFilterFactory;

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) 
        http.csrf(ServerHttpSecurity.CsrfSpec::disable);
        return http.build();
    

    @Bean
    RouteLocator routeLocator(final RouteLocatorBuilder routeLocatorBuilder) 
        return routeLocatorBuilder.routes()
                .route("subscriptions", subscriptionsRoute())
                .build();
    

    private Function<PredicateSpec, Buildable<Route>> subscriptionsRoute() 
        return spec -> spec
                .path("/subscriptions/**")
                .filters(s -> s.filter(tokenFilterFactory.apply()).prefixPath("/v1"))
                .uri(subscriptionsUrl);
    


还有测试课:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = PnApiGatewayApp.class)
@AutoConfigureStubRunner(ids = "io.mkrzywanski:subscription-app:+:stubs", stubsMode = StubRunnerProperties.StubsMode.CLASSPATH)
@ActiveProfiles("test")
class SubscriptionSpec 

    private WebTestClient webClient;

    @LocalServerPort
    private int port;

    @StubRunnerPort("io.mkrzywanski:subscription-app")
    private int stubRunnerPort;

    @Autowired
    ConfigurableEnvironment environment;

    @BeforeEach
    void setup() 
        String baseUri = "http://localhost:" + port;
        this.webClient = WebTestClient.bindToServer()
                .responseTimeout(Duration.ofSeconds(10))
                .baseUrl(baseUri).build();
    

    @Test
    void  test() 
        String body = "\"userId\":\"22e90bbd-7399-468a-9b76-cf050ff16c63\",\"itemSet\":[\"value\":\" Rainbow Six\"]";
        var response = webClient.post()
                .uri("/subscriptions")
                .header("Authorization", "Bearer xxx")
                .header("Content-type", MediaType.APPLICATION_JSON_VALUE)
                .bodyValue(body)
                .exchange()
                .expectStatus().isCreated()
                .expectBody(String.class)
                .value(Matchers.equalTo("\"subscriptionId : \"6d692849-58fd-439b-bb2c-50a5d3669fa9\"\""));
    

理想情况下,我希望 subscriptions.url 属性集 after 配置存根运行器,但 before 我的网关配置由 Spring 选择,以便 url 重定向起作用。

我已经尝试使用ApplicationContextInitializer,但是当初始化程序实例启动时,StubRunnerPort 似乎尚未配置。

所以问题是 - 如何获取存根运行器端口并使用它来将其注入其他服务 url,以便网关将请求路由到测试中的存根运行器?

【问题讨论】:

【参考方案1】:

解决方案 1

这可以通过使用 application-test.yml 文件来实现,该文件具有使用替换的已定义 url 属性。 application-test.yml 文件。该方法描述为here:

subscriptions:
  url: http://localhost:$stubrunner.runningstubs.io.mkrzywanski.subscription-app.port

这里stubrunner.runningstubs.io.mkrzywanski.subscription-app.port 将用作存根端口,因此可以替换它。无需更改配置。

解决方案 2(需要更多代码)

我通过创建一个测试配置使其工作,该配置扩展了包含 url 属性和 RouteLocator 配置并依赖于 batchStubRunner bean 的配置:

@DependsOn("batchStubRunner")
@EnableAutoConfiguration
@Import(LoggingFilter.class)
class GatewayTestConfig extends GatewayConfig implements InitializingBean 

    @Autowired
    ConfigurableEnvironment environment;

    @Override
    public void afterPropertiesSet() 
        this.subscriptionsUrl = "http://localhost:" + environment.getProperty("stubrunner.runningstubs.io.mkrzywanski.subscription-app.port");
    

这里的重点是:

配置仅在batchStubRunner bean 可用后运行,因此可以在environment 中找到 StrubRunner 的端口 配置实现了InitializingBean,所以我可以覆盖subscriptionsUrl,它现在是父配置中的protected subscriptionsUrl 被覆盖后 - 它可用于从父配置配置 RouteLocator bean。

现在的测试是这样的:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = GatewayTestConfig.class)
@AutoConfigureStubRunner(ids = "io.mkrzywanski:subscription-app:+:stubs", stubsMode = StubRunnerProperties.StubsMode.CLASSPATH)
@ActiveProfiles("test")
class SubscriptionSpec 

    private WebTestClient webClient;

    @LocalServerPort
    private int port;

    @BeforeEach
    void setup() 
        String baseUri = "http://localhost:" + port;
        this.webClient = WebTestClient.bindToServer()
                .responseTimeout(Duration.ofSeconds(10))
                .baseUrl(baseUri).build();
    

    @Test
    void shouldRouteToSubscriptions() 

        String body = "\"userId\":\"22e90bbd-7399-468a-9b76-cf050ff16c63\",\"itemSet\":[\"value\":\"Rainbow Six\"]";
        webClient.post()
                .uri("/subscriptions")
                .header("Accept", MediaType.APPLICATION_JSON_VALUE)
                .header("Authorization", "Bearer xxx")
                .header("Content-type", MediaType.APPLICATION_JSON_VALUE)
                .bodyValue(body)
                .exchange()
                .expectStatus().isCreated()
                .expectBody()
                .jsonPath("$.subscriptionId").exists()
                .jsonPath("$.subscriptionId").value(IsUUID.UUID());
    

【讨论】:

以上是关于如何在 spring-cloud-gateway 合约测试中从 spring-cloud-contract 中设置带有 StubRunner 端口的 url的主要内容,如果未能解决你的问题,请参考以下文章

Spring-Cloud-Gateway 创建的初始 Trace 都命名为“/”,无论路径如何

无法根据活动的 Spring 配置文件生成不同的 spring-cloud-gateway 路由

java学习---spring-cloud-gateway网关配置

spring-cloud-gateway使用https注意事项---设置证书和需要注意的问题

sentinal spring-cloud-gateway adapter(1.6)异常错误之@EnableCircuitBreaker

spring-cloud-gateway之GatewayFilterFactory