In my spring boot project, I am using a library (as dependency) to communicate with a kubernetes cluster. The library name is fabric8-kubernetes-client, added it as a dependency:
implementation("io.fabric8:kubernetes-client:6.9.0")
I created a configuration class to serve the KubernetesClient bean:
@Configuration
public class K8sClientConfig {
@Bean
public KubernetesClient kubernetesClient() {
// it will establish a connection to a k8s cluster
return new KubernetesClientBuilder().build();
}
}
Another @Component class injects above bean via auto-wiring on constructor:
@Component
public class ProductManager {
private KubernetesClient kubernetesClient;
@Autowired
public ProductManager(KubernetesClient kubernetesClient) {
this.kubernetesClient = kubernetesClient;
}
public void createConfigMap() {
// using the kubernetesClient to create ConfigMap in cluster, pseudo code for simplicity
ConfigMap cm = ...;
kubernetesClient.configMaps().resource(cm).create();
}
// I created this function only for my unit test to inject mocked instance of KubernetesClient, but I really don't like this way...
public void setKubernetesClient(KubernetesClient client) {
this.kubernetesClient = client
}
}
All works fine when running the application.
But now, I want to write some unit tests, I really want to use mocked KubernetesClient so that there is no real kubernetes connection needed when running tests.
So, I use the associated kubernetes-server-mock dependency:
testImplementation("io.fabric8:kubernetes-server-mock:6.9.0")
In my test class, I created a mocked KubernetesClient:
@SpringBootTest
@EnableKubernetesMockClient(crud = true)
public class ProductManagerTest {
// mocked instances, because of the annotation @EnableKubernetesMockClient, this is the way the library is suggesting for mocking
private KubernetesClient kubernetesClient;
private KubernetesMockServer server;
@Autowired
private ProductManager productManager;
@BeforeEach
public void beforeTest() {
// inject mocked client using that ugly function before each test run
productManager.setKubernetesClient(kubernetesClient);
}
@Test
public void testCreateConfigMap() {
createConfigMap();
//assertion to check configMap is created
...
}
}
The above unit test runs well if it can reach the real kubernetes cluster. That's the problem I need to solve, because even I am using mocked client & injected it in an "ugly" way(that setKubernetesClient(...) function), but due to the @SpringBootTest will load all beans, which means that configuration class K8sClientConfig above still execute new KubernetesClientBuilder().build() which looks for real connection.
I wonder is it possible to inject the KubernetesClient bean conditionally, like:
@Configuration
public class K8sClientConfig {
@Bean
public KubernetesClient kubernetesClient() {
if(NOT_RUNNING_TEST) {
return new KubernetesClientBuilder().build();
} else {
// somehow create and return the mocked KubernetesClient
}
}
}
How to achieve this if possible??
If above thoughts is not possible what could be the other better solutions?