Inject mocked instance of KubernetesClient object while running unit test

210 Views Asked by At

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?

0

There are 0 best solutions below