Open eduanb opened 2 years ago
Thanks for the suggestion.
Adding a test for Kubernetes in ConditionalOnCloudPlatformTests is currently not possible
It is possible, although it's quite cumbersome. It can be achieved by using a custom supplier for the runner's context and replacing the system environment property source:
@Test
void outcomeWhenKubernetesPlatformPresentShouldMatch() {
new ApplicationContextRunner(() -> {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
Map<String, Object> systemEnvironment = new HashMap<>();
systemEnvironment.put("KUBERNETES_SERVICE_HOST", "k8s.example.com");
systemEnvironment.put("KUBERNETES_SERVICE_PORT", "4567");
context.getEnvironment().getPropertySources()
.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, new MapPropertySource(
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, systemEnvironment));
return context;
}).withUserConfiguration(KubernetesPlatformConfig.class).run((context) -> assertThat(context).hasBean("foo"));
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
static class KubernetesPlatformConfig {
@Bean
String foo() {
return "foo";
}
}
We can consider removing some of the boilerplate here by providing methods on the runner specifically for manipulating the system environment. I'm not sure how common this use-case is, so let's see if the rest of the team thinks making it easier is worthwhile.
The use case does not seem very common so I wonder if it warrants a dedicated method on the runner. I am also sightly worried that systemEnvironment
and systemProperties
can confuse users. The runner has higher-level way to customize things before the context run (Function
). Perhaps we could offer an implementation on the side that users can apply for this use case?
Thanks, @snicoll. I'm not sure that the Function
would help here as there's no good way to plug this into an existing ApplicationContextRunner
. withInitializer
does not work for the example above as @ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
is evaluated before the initializer is called. Confusingly, this does work:
@Test
void outcomeWhenKubernetesPlatformPresentShouldMatch() {
new ApplicationContextRunner().withInitializer((context) -> {
Map<String, Object> systemEnvironment = new HashMap<>();
systemEnvironment.put("KUBERNETES_SERVICE_HOST", "k8s.example.com");
systemEnvironment.put("KUBERNETES_SERVICE_PORT", "4567");
context.getEnvironment().getPropertySources()
.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, new MapPropertySource(
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, systemEnvironment));
}).withUserConfiguration(KubernetesPlatformConfig.class).run((context) -> assertThat(context).hasBean("foo"));
}
@Configuration(proxyBeanMethods = false)
static class KubernetesPlatformConfig {
@Configuration(proxyBeanMethods = false)
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
static class SomeInnerConfiguration {
@Bean
String foo() {
return "foo";
}
}
}
Nesting the use of @ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
defers its evaluation until after the initializer has been called.
Flagging for a team meeting as I'd like to discuss whether we should revise the ordering in the runner:
The current arrangement means that conditions on the registered classes themselves are evaluated before the initializers and bean registrations are applied.
I've opened https://github.com/spring-projects/spring-boot/issues/31280 to re-order things in ApplicationContextRunner
. We'll leave this issue open for now while we decide if we want to do anything to ease configuring the system environment.
For some specific cases, it is necessary to set systemEnvironment which is different from systemProperties. For example, mocking out a Kubernetes environment. Adding a test for Kubernetes in ConditionalOnCloudPlatformTests is currently not possible.