I have a spring cloud gateway application which routes requests to another service. Another service defines contracts which are imported as stubs by spring cloud gateway application in tests.
Now I would like to have contract tests in my gateway that will consume the stubs of another service. The problem is that I do not know how to inject the StubRunnerPort
as property/environment so it can be picked by my configuration class and configure the routes accordingly :
Api gateway routes configuration
@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);
}
}
And the test 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\"\"}"));
}
Ideally I would like to have subscriptions.url
property set after stub runner is configured but before my gateway configuration is picked by Spring so that url redirects will work.
I have already tried to use ApplicationContextInitializer
but it seems that StubRunnerPort is not configured yet, when instance of initializer is launched.
So the question is - how to get stub runner port and use it to inject it into other services url, so that gateway would route the requests to the stub runner in tests?
CodePudding user response:
I made it work by creating a test configuration which extends configuration that contains url properties and RouteLocator
configuration and has a dependency on 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");
}
}
The key points here are :
- the configuration is run only after
batchStubRunner
bean is available so the port of StrubRunner can be found in theenvironment
- The configuration implements
InitializingBean
so I am able to override thesubscriptionsUrl
which is nowprotected
in parent configuration - After
subscriptionsUrl
is overriden - it can be used to configureRouteLocator
bean from parent configuration.
The test looks like this now :
@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());
}
}