HTTP clients
Mock calls made with your own http.Client, and run mocked tests in parallel.
By default, mocks replace http.DefaultTransport. That covers http.Get, http.DefaultClient and every http.Client created without a Transport.
A client with its own transport, such as one configured with custom TLS or connection pooling settings, bypasses http.DefaultTransport. Pass it to the test with HttpClient so its transport is replaced instead:
type UserClient struct {
http *http.Client
}
func (c *UserClient) Handler() http.Handler {
return forwardWith(c.http, func() *http.Request {
return newRequest(http.MethodGet, "http://users/api/user/1234", "")
})
}
func TestMocks_CustomHTTPClient(t *testing.T) {
client := &http.Client{Timeout: 5 * time.Second}
app := &UserClient{http: client}
getUser := apitest.NewMock().
Get("http://users/api/user/1234").
RespondWith().
Status(http.StatusOK).
End()
apitest.New().
HttpClient(client).
Mocks(getUser).
Handler(app.Handler()).
Get("/").
Expect(t).
Status(http.StatusOK).
End()
}
The client's original transport is restored when the test ends.
Most applications create their clients at start up. Make the client injectable, as a field or a constructor argument, so the test can pass the same client to the application and to apitest.
Parallel tests
http.DefaultTransport is shared by the whole process. Two tests that replace it at the same time interfere with each other, so tests that mock the default transport must not call t.Parallel().
To run mocked tests in parallel, give each test its own client and pass it with HttpClient:
for _, id := range []string{"1", "2", "3"} {
t.Run(id, func(t *testing.T) {
t.Parallel()
// each subtest has its own client, so the mocks do not
// replace the process-wide http.DefaultTransport
client := &http.Client{}
handler := forwardWith(client, func() *http.Request {
return newRequest(http.MethodGet, "http://users/api/user/"+id, "")
})
getUser := apitest.NewMock().
Get("http://users/api/user/" + id).
RespondWith().
Status(http.StatusOK).
End()
apitest.New().
HttpClient(client).
Mocks(getUser).
Handler(handler).
Get("/").
Expect(t).
Status(http.StatusOK).
End()
})
}Clients that are not net/http
Mocks work at the http.RoundTripper level. SDKs built on net/http, such as most cloud and API clients, can be mocked if they let you supply the *http.Client. Clients that use a different networking stack, such as gRPC, cannot be mocked this way.