How do you unit-test HTTP handlers and HTTP clients with net/http/httptest?
Question 473MediumGo 1.22 to 1.25
For handlers, use httptest.NewRecorder() and httptest.NewRequest — no network, fast, and you inspect the recorded status, headers and body. Route through your real mux so Go 1.22 path values are populated (or call req.SetPathValue). For clients, spin up httptest.NewServer (a real loopback server) and point the client's base URL at srv.URL.
func TestGetItem(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("GET /items/{id}", getItem)
tests := []struct {
path string
want int
}{
{"/items/42", http.StatusOK},
{"/items/abc", http.StatusBadRequest},
}
for _, tc := range tests {
t.Run(tc.path, func(t *testing.T) {
t.Parallel()
rec := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, tc.path, nil)
mux.ServeHTTP(rec, req)
if rec.Code != tc.want {
t.Fatalf("status = %d, want %d; body=%q", rec.Code, tc.want, rec.Body.String())
}
})
}
}
func TestClientRetriesOn503(t *testing.T) {
var calls atomic.Int32
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if calls.Add(1) == 1 {
w.WriteHeader(http.StatusServiceUnavailable)
return
}
io.WriteString(w, `{"ok":true}`)
}))
t.Cleanup(srv.Close)
c := NewAPIClient(srv.URL, srv.Client())
if err := c.Ping(context.Background()); err != nil {
t.Fatal(err)
}
if got := calls.Load(); got != 2 {
t.Fatalf("calls = %d, want 2", got)
}
}
Design tip interviewers like: accept an *http.Client or a small interface (Do(*http.Request) (*http.Response, error)) via constructor injection so tests can substitute it; httptest.NewTLSServer tests TLS paths.
More on Standard Library, HTTP & Systems Design in Go
- Q471Design an in-process pub/sub broker. How do you handle slow subscribers?
- Q472Fan out N HTTP calls with bounded concurrency, fail fast on first error, and collect results in order.
- Q474Implement Server-Sent Events (SSE) in net/http. How do http.ResponseController, flushing and WriteTimeout interact?
- Q475Write an HTTP client retry helper with exponential backoff and jitter. What makes a request safe to retry?
- Q476Build a reverse proxy with httputil.ReverseProxy. Why is Rewrite preferred over Director?
- Q477How do you test time-dependent concurrent code deterministically? Explain testing/synctest (Go 1.25).