0

I am using gomock to mock an http function that is called within the function under test. While it works great for one call it seems to call the first function again when I want to return a different result on the second call (see code below). The function calls the first mocked function (in calls slice) n number of times, so my question is: How can I use gomock to make a mocked function return different results on subsequent calls? I used this stackoverflow post for inspiration.

This is what I have until now:

ctrl := gomock.NewController(t)
defer ctrl.Finish()

// Mock http client
mockHttp := mock_http.NewMockHttp(ctrl)

// set up order of mock calls
var calls []*gomock.Call
for i := 0; i < len(tc.mockHttpResps); i++ {
    // Note: tc.mockHttpResps[i] is a *http.Response{}
    fn := mockHttp.EXPECT().Post(gomock.Any(), gomock.Any(),gomock.Any()).Return(tc.mockHttpResps[i], tc.mockHttpErrs[i]).Times(1) 
    calls = append(calls, fn)
}
gomock.InOrder(
   calls...
)

But it produces an error along these lines:

Unexpected call to *mock_rpc.MockHttp.Post because [function] has already been called the max number of times

If I try to set the calls to .AnyTimes() then the error goes away but the first mocked function is still called multiple times. I then get other errors such as EOF as the http.Response body is read twice, which is to be expected.

Note: I have tried to do exactly as in the linked post, but it leads to the same error.

124141251
  • 31
  • 5
  • 1
    Don't mock HTTP requests/responses. There's no need. The standard library has tools to use _real_ requests and responses. See the [`net/http/httptest` package](https://pkg.go.dev/net/http/httptest). Or here are a couple videos on the topic: [1](https://youtu.be/ebEfF1wzc54), [2](https://youtu.be/tCAl8fdqDnM) – Jonathan Hall May 24 '23 at 20:10
  • @JonathanHall Thanks, so there is no need to inject http clients as you can easily mock them using httptest. Did I understand that correctly? Great videos btw – 124141251 May 25 '23 at 08:12
  • That's mostly right, except that `httptest` doesn't mock anything. It just creates _real_ responses/requests as you desire. – Jonathan Hall May 25 '23 at 08:32

1 Answers1

1

As JonathanHall pointed out there is no need to mock http clients.

This is the revised and working code:

cnt := 0
// mock http server
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    if tc.mockHttpErrs[cnt] != nil {
        w.WriteHeader(http.StatusBadRequest)
        fmt.Fprintln(w, tc.mockHttpErrs[cnt].Error())
        cnt++
        return
    }
    fmt.Fprintf(w, tc.mockHttpResps[cnt])
    cnt++
}))
defer svr.Close()

// use svr.URL and svr.Client() to inject into whatever code needs it

This also allows you to be more flexible regarding the responses without relying on gomock!

124141251
  • 31
  • 5