How to write a unit test to mock clientset for a http call to return all nodes in a kubernetes cluster

714 Views Asked by At

I am currently working on getting familiar with Ginkgo testing framework to write unit tests for api calls and just plain functions. Having trouble wrapping my head around mocking things.

Right now I have a GET call that returns a list of the nodes that are running in a kubernetes cluster. I am currently trying to write a unit test to validate that the call returns the right responses. The issue that I am running into is how the heck to mock the call to setupKube to only return a fakeclientset() with the object I want. For instance a set of three nodes. I have a unit test to the kube.GetAllNodes function where I just pass in a fakeclientset() but here I am trying to mock setupKube to only return afake client set so it then gets passed down and returns what I need it to for my unit tests. I've looked at monkey patching, but it isn't clicking on how I would go about implementing that to mock the return.

func GetAllNodes(c *gin.Context) {
    setHeaders(c)
    clientset, err := setupKube()
    if err != nil {
        errorResponse := buildErrors(http.StatusInternalServerError, err)
        c.JSON(errorResponse.HTTPStatusCode, errorResponse)
        return
    }

    nodes, err := kube.GetAllNodes(clientset)
    if err != nil {
        errorResponse := buildErrors(http.StatusInternalServerError, err)
        c.JSON(errorResponse.HTTPStatusCode, errorResponse)
        return
    }
    if len(nodes) == 0 {
        c.JSON(http.StatusNoContent, nodes)
        return
    }
    c.JSON(http.StatusOK, nodes)
}

// Private Functions
func setupKube() (kubernetes.Interface, error) {
    // checks for pod running in cluster
    config, err := rest.InClusterConfig()
    if err != nil {
        return nil, fmt.Errorf("error creating in-cluster config: %v", err)
    }
    // creates the clientset
    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        return nil, fmt.Errorf("error creating clientset: %v", err)
    }

    return clientset, nil
}

I have been looking at different techniques but they aren't 100% clicking my head with things like monkey patching, or higher order functions. Right now what happens is if I go to run the unit test it explodes because it isn't running in a cluster configuration which makes sense since it isn't. I am just stuck on the mocking aspect.

1

There are 1 best solutions below

0
Tom On

The way that I mocked this in my test cases is as below:

var GetClient = func() (kubernetes.Interface, error) {
// checks for pod running in cluster
config, err := rest.InClusterConfig()
if err != nil {
    return nil, fmt.Errorf("error creating in-cluster config: %v", err)
}
// creates the clientset
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
    return nil, fmt.Errorf("error creating clientset: %v", err)
}

return clientset, nil

}

So I set it to a variable, then in the unit test, I created my own mock function and set it to a different one that I created. So the unit test call looks like this:

var _ = Describe("Kubernetes Handler", func() {
var (
    fakeClientset kubernetes.Interface
    fakeErr       error
    router        *gin.Engine
    w             *httptest.ResponseRecorder
)

var mockGetClient = func() (kubernetes.Interface, error) {
    return fakeClientset, fakeErr
}

var mockFailedGetClient = func() (kubernetes.Interface, error) {
    return nil, fmt.Errorf("Error")
}

BeforeEach(func() {
    router = gin.Default()
    router.GET("/rest/v1/k8s/pods", handlers.GetAllNodes)
    w = httptest.NewRecorder()
})

Context("when the request returns no nodes", func() {
    // Arrange
    BeforeEach(func() {
        fakeClientset = fake.NewSimpleClientset()
        fakeErr = nil
        handlers.GetClient = mockGetClient
        req, _ := http.NewRequest("GET", "/rest/v1/k8s/pods", nil)
        router.ServeHTTP(w, req)
    })
    It("returns 204", func() {
        // Act
        Expect(w.Code).To(Equal(http.StatusNoContent))
    })
})

So now as you see in the code above I am basically saying, don't call that func, use mine instead. Which returns a clientset of kubernetes.Interface which I replace with a fake one which then returns nothing since I passed nothing into it.