How to mock http.Client Do method

57,277

Solution 1

Any struct with a method matching the signature you have in your interface will implement the interface. For example, you could create a struct ClientMock

type ClientMock struct {
}

with the method

func (c *ClientMock) Do(req *http.Request) (*http.Response, error) {
    return &http.Response{}, nil
}

You could then inject this ClientMock struct into your GetOverview func. Here's an example in the Go Playground.

Solution 2

The net/http/httptest package is your best friend:

// generate a test server so we can capture and inspect the request
testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
    res.WriteHeader(scenario.expectedRespStatus)
    res.Write([]byte("body"))
}))
defer func() { testServer.Close() }()

req, err := http.NewRequest(http.MethodGet, testServer.URL, nil)
assert.NoError(t, err)

res, err := http.DefaultClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, scenario.expectedRespStatus, res.StatusCode, "status code should match the expected response")

Solution 3

You have to create a struct with methods that match interface. Mocks are commonly used for testing purposes, therefore people want the ability to prepare return values of mock methods. To achieve this, we create struct with func attributes corresponding to methods.

As your interface is:

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

Equivalent mock:

type MockClient struct {
    DoFunc func(req *http.Request) (*http.Response, error)
}

func (m *MockClient) Do(req *http.Request) (*http.Response, error) {
    if m.DoFunc != nil {
        return m.DoFunc(req)
    }
    return &http.Response{}, nil
}

Then, next step is to write some tests. Example here.

Solution 4

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

Generally to mock HTTP requests I recommend starting up a real HTTP server locally, since in Go this is easy to do. https://golang.org/pkg/net/http/httptest/ is a pretty standard way to do that (see the example code given under the Server type).

However having done a lot of HTTP mocking I wanted something that does a little more, like a good mock library would: 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.

Share:
57,277
quentino
Author by

quentino

Updated on July 22, 2020

Comments

  • quentino
    quentino almost 4 years

    I'm trying to find a solution to write test and mock HTTP response. In my function where I accept interface:

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

    I makes http get request with base auth

    func GetOverview(client HttpClient, overview *Overview) (*Overview, error) {
    
        request, err := http.NewRequest("GET", fmt.Sprintf("%s:%s/api/overview", overview.Config.Url, overview.Config.Port), nil)
        if (err != nil) {
            log.Println(err)
        }
        request.SetBasicAuth(overview.Config.User, overview.Config.Password)
        resp, err := client.Do(request)
    

    How can I mock this HttpClient? I'm looking for mock library, for instance: https://github.com/h2non/gock but there is only mock for Get and Post

    Maybe I should do it in a different way. I'll be grateful for advice