Go cookbook · Beginner

Test HTTP handlers with httptest

Handler tests start real servers on ports or skip routing and middleware.

Create a free account to save this page, take notes and track your progress. Sign in

Approach

Build the real handler (mux plus middleware), send requests with httptest.NewRequest and a ResponseRecorder, and assert status, headers and body. Use httptest.NewServer to fake external APIs.

Solution

package api

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

func NewHandler() http.Handler {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /items/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "1" {
			http.Error(w, "not found", http.StatusNotFound)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]string{"name": "Teapot"})
	})
	return mux
}

func TestGetItem(t *testing.T) {
	h := NewHandler()

	rec := httptest.NewRecorder()
	h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/items/1", nil))
	if rec.Code != http.StatusOK {
		t.Fatalf("status = %d, want 200", rec.Code)
	}
	if got := strings.TrimSpace(rec.Body.String()); got != `{"name":"Teapot"}` {
		t.Errorf("body = %s", got)
	}

	rec = httptest.NewRecorder()
	h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/items/2", nil))
	if rec.Code != http.StatusNotFound {
		t.Errorf("status = %d, want 404", rec.Code)
	}

	rec = httptest.NewRecorder()
	h.ServeHTTP(rec, httptest.NewRequest(http.MethodDelete, "/items/1", nil))
	if rec.Code != http.StatusMethodNotAllowed {
		t.Errorf("status = %d, want 405", rec.Code)
	}
}

Pitfalls

  • Testing handler functions directly and missing routing and middleware behaviour.
  • Comparing JSON as strings when field order or whitespace can change; decode and compare values.
  • Tests calling real external services.

Variations

  • httptest.NewServer(handler) gives a real URL for clients that need one (fake third-party APIs).
  • Use testing/synctest (Go 1.25) to test code with timers deterministically.

Further reading: httptest

Reference: https://pkg.go.dev/net/http/httptest

More Go recipes

Was this page helpful?