0

Hey so I have seen and used this post to help mock my http.Client but when I try to pass the mock request I get the following error: cannot use mockClient (variable of type *MockClient) as *"net/http".Client value in argument to api.callAPI.

In one file I have my actual code:

I created the HTTPClient:

type HTTPClient interface {
    Do(req *http.Request) (*http.Response, error)
}

I have the function that passes the HTTPClient as an interface (I can't show all of it because it is for work but here's the important pieces):

func (api *API) callAPI(req *http.Request, client HTTPClient) (utils.ErrorWrapper, bool) {
response, err := client.Do(req)
}

I also have another function that calls the callAPI method. In that function I create client variable right before I call the callAPI function

var Client HTTPClient = &http.Client{}
response, isRetry := api.callAPI(req, Client)

This all works fine. However, in my testing file I get the error as mentioned above. I am using testify for my mocking framework. Here is what I have in my testing file (both the testing file and the actual code are apart of the same package):

set up my mock client and the Do function using testify

type MockClient struct {
    mock.Mock
}

func (m *MockClient) Do(req *http.Request) (*http.Response, error) {
    args := m.Called()
    resp := args.Get(0)
    return resp.(*http.Response), args.Error(1)
}

Then create my test:

func TestCallAPI(t *testing.T) {

    mockClient := &MockClient{}

    recorder := httptest.NewRecorder()
    responseCh := make(chan utils.ErrorWrapper)
    c, _ := gin.CreateTestContext(recorder)
id:= "unitTest123"


    api := NewAPICaller(responseCh, id, c)

    var response = Response{
        StatusCode: 200,
    }
    //setup expectations
    mockClient.On("Do").Return(response, nil)

    req, _ := http.NewRequest("GET", "URL I Can't Show", nil)


    wrapper, isRetry := api.callAPI(req, mockClient)
    mockClient.AssertExpectations(t)
    assert.Equal(t, "placeholder", wrapper)
    assert.Equal(t, false, isRetry)

}

I tried to do a similar thing with the mockclient variable the way I did with the Client variable:

var mockclient HTTPClient = &MockClient{}

but I get this error on the HTTPClient: undeclared name: HTTPClient. Unsure why this is happening because they are a part of the same package so I thought it could be exported easily?

maura
  • 17
  • 3
  • 1
    Not what you asked for but anyway a good advise: Do not mock that much. Set up a HTTP server via net/http/httptest.Server and actually _make_ the HTTP call. This allows you to keep the tests much simpler. Mocking is _not_ the best test strategy in Go. – Volker Mar 11 '21 at 13:33
  • @Volker I'm willing to trying anything so this is helpful. My entire team is new to go - so I don't think we were aware of Mocking not being the best strategy. Thanks! – maura Mar 11 '21 at 13:36
  • as far as packages go : are the files you mention in the same directory (I guess they are, just checking) ? what's the package name for the file which declares the `HTTPClient` interface and the package name for the test file ? "package name" meaning : the word `xxx` mentioned in the first statement `package xxx` for each of the files – LeGEC Mar 11 '21 at 13:41
  • @LeGEC hey yeah package names are the same. I even double checked by copying and pasting the package name from the original file to the test file just incase i accidentally spelled something wrong and i still get that error. – maura Mar 11 '21 at 14:16
  • How do you build and test your code? Do you do literally just `go build` and `go test` ? Or do you use some other command? Maybe something like `go test foo_test.go`? Note that working with files is **dangerous** and doesn't work like expected most of the time. – Volker Mar 11 '21 at 14:20

1 Answers1

-2

There are 2 ways you can achieve this.

  1. As suggested by @Volker. Setup a HTTP Server. you can use a docker container to run it for your tests.

  2. The other options (my favorite for unit testing) is to wrap your HTTP client in an interface of your own. That interface will expose methods either your domain specific OR probably just plain simple GET, POST. Then use the library like mockgen to generate mock. This library generate mocks based on interface. With this setup in place, you can have proper unit tests on the exposed methods.

Em Ae
  • 8,167
  • 27
  • 95
  • 162