How to mock http.Client that returns a JSON response

Viewed 10114

I'm trying to test a method that uses net/http to make requests. Specifically what I'm trying to achieve is to inject a mock http.Client that responds with a certain JSON body

type clientMock struct{}

func (c *clientMock) Do(req *http.Request) (*http.Response, error) {
  json := struct {
    AccessToken string `json:"access_token`
    Scope       string `json:"scope"`
  }{
    AccessToken: "123457678",
    Scope:       "read, write",
  }
  body := json.Marshal(json)
  res := &http.Response {
    StatusCode: http.StatusOK,
    Body:       // I haven't got a clue what to put here
  }
  return res
}

func TestRequest(t *testing.T) { //tests here }

I do know that the Body is of a type io.ReadCloser interface. Trouble is I can't for the life of me find a way to implement it in the mock body response.

Examples as found here so far only demonstrates returning a blank &http.Response{}

3 Answers

While it's probably more useful to mock the full request cycle with httptest.Server, you can use ioutil.NopCloser to create the closer around any reader:

Body: ioutil.NopCloser(bytes.NewReader(body))

and if you want an empty body, just provider a reader with no content.

Body: ioutil.NopCloser(bytes.NewReader(nil))

In your test file (my_test.go):

type MyJSON struct {
        Id          string
        Age         int
}

// Interface which is the same as httpClient because it implements "Do" method.
type ClientMock struct {}

func (c *ClientMock) Do(req *http.Request) (*http.Response, error) {
    mockedRes := MyJSON {"1", 3}

    // Marshal a JSON-encoded version of mockedRes
    b, err := json.Marshal(mockedRes)
    if err != nil {
        log.Panic("Error reading a mockedRes from mocked client", err)
    }

    return &http.Response{Body: ioutil.NopCloser(bytes.NewBuffer(b))}, nil
}

// your test which will use the mocked response
func TestMyFunction(t *testing.T) {

    mock := &ClientMock{}
    actualResult := myFunction(mock)
    assert.NotEmpty(t, actualResult, "myFunction should have at least 1 result")

}

In your implementation (main.go):

package main

import (
    "net/http"
)

func main() {
    myFunction(&http.Client{})
}

I know it's been a little while but I just wrote something to help with this recently.

Like JimB I recommend starting up a real HTTP server locally, since in Go this is easy to do with https://golang.org/pkg/net/http/httptest/.

However having done a lot of HTTP mocking I wanted something that does a little more, like a good mock library would: returning specific data, easy setting of expectations, validation that all requests were made, etc. I have generally used https://godoc.org/github.com/stretchr/testify/mock for mocking and wanted features like that.

So I wrote https://github.com/dankinder/httpmock, which basically combines the two. If you just want a mock that accepts JSON and spits out JSON, it may be an easier way to go.

Related