Skip to content

Testing

The httpclienttest package routes the requests of the clients to an http.Handler, in memory: tests need no network nor fake server, and the requests still go through the middlewares, the instrumentation and the request log.

Serve

httpclienttest.Serve replaces the base transport of the application: every client of the factory sends its requests to the handler.

func TestTemperature(t *testing.T) {
t.Parallel()
var (
weather *WeatherClient
spans *tracetest.InMemoryExporter
logs *logtest.InMemoryExporter
)
coretest.Start(t, core.New(httpclient.Module, fx.Provide(NewWeatherClient)),
httpclienttest.Serve(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/cities/paris", r.URL.Path)
fmt.Fprint(w, `{"celsius": 21}`)
})),
fx.Populate(&weather, &spans, &logs),
)
temperature, err := weather.Temperature(t.Context(), "paris")
require.NoError(t, err)
assert.Equal(t, 21, temperature.Celsius)
spantest.HasSpan(t, spans,
spantest.WithName("GET"),
spantest.WithAttributeValue(semconv.HTTPResponseStatusCode(http.StatusOK)),
)
logtest.HasRecord(t, logs, logtest.WithBody("http client request"), logtest.WithAttributeValue("status", "200"))
}

The handler gets a server request, like from a real server: r.Host, r.RequestURI and r.RemoteAddr (httpclienttest.RemoteAddr) are set. Route the requests of several APIs with an http.ServeMux, using host patterns: mux.Handle("weather.example.com/", ...).

A client built with httpclient.WithTransport keeps its own transport: Serve does not apply to it.

Transport

httpclienttest.Transport returns the in-memory http.RoundTripper, for a single client:

client := factory.NewClient(httpclient.WithTransport(httpclienttest.Transport(handler)))

Calling the application server

To test a client against the HTTP server of the application, serve its requests with the server handler. The trace context is propagated, so the server span is a child of the client span:

var (
factory *httpclient.Factory
server *http.Server
spans *tracetest.InMemoryExporter
)
coretest.Start(t, core.New(httpclient.Module, httpserver.Module, users.Module), fx.Populate(&factory, &server, &spans))
client := factory.NewClient(httpclient.WithTransport(httpclienttest.Transport(server.Handler)))

Errors

Test the transport errors with a failing transport:

client := factory.NewClient(httpclient.WithTransport(httpclient.RoundTripperFunc(func(*http.Request) (*http.Response, error) {
return nil, errors.New("connection refused")
})))

A canceled request context fails the request with the context error, like with a real transport.