如何在 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
我有一个 spring 云网关应用程序,可以将请求路由到另一个服务。另一项服务定义了在测试中由 spring 云网关应用程序作为存根导入的合同。
现在我想在我的网关中进行合同测试,以使用另一个服务的存根。问题是我不知道如何将 StubRunnerPort
注入为 property/environment 所以它可以被我的配置 class 选中并相应地配置路由:
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);
}
}
测试 class :
@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 stub runner 已配置,但 before 我的网关配置由 Spring 选择,因此 url 重定向将起作用。
我已经尝试使用 ApplicationContextInitializer
但似乎 StubRunnerPort 尚未配置,当启动初始化程序实例时。
所以问题是 - 如何获取存根运行器端口并使用它将其注入其他服务url,以便网关将请求路由到测试中的存根运行器?
解决方案 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
将作为 Stub 端口可用,因此可以被替换。不需要更改配置。
解决方案 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
,所以我可以覆盖现在 protected
在父配置 中的 subscriptionsUrl
- 覆盖
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 云网关应用程序,可以将请求路由到另一个服务。另一项服务定义了在测试中由 spring 云网关应用程序作为存根导入的合同。
现在我想在我的网关中进行合同测试,以使用另一个服务的存根。问题是我不知道如何将 StubRunnerPort
注入为 property/environment 所以它可以被我的配置 class 选中并相应地配置路由:
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);
}
}
测试 class :
@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 stub runner 已配置,但 before 我的网关配置由 Spring 选择,因此 url 重定向将起作用。
我已经尝试使用 ApplicationContextInitializer
但似乎 StubRunnerPort 尚未配置,当启动初始化程序实例时。
所以问题是 - 如何获取存根运行器端口并使用它将其注入其他服务url,以便网关将请求路由到测试中的存根运行器?
解决方案 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
将作为 Stub 端口可用,因此可以被替换。不需要更改配置。
解决方案 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
,所以我可以覆盖现在protected
在父配置 中的 - 覆盖
subscriptionsUrl
后 - 它可用于从父配置配置RouteLocator
bean。
subscriptionsUrl
测试现在看起来像这样:
@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());
}
}