Skip to content

Guide

Testing

Test routes, middleware, and error handling with the standard net/http/httptest package.

A Zinc app is an http.Handler, so you test it with net/http/httptest and no extra tooling. Build the app the same way production does, send it a request, and assert on the response.

func TestGreeting(t *testing.T) {
app := zinc.New()
app.Get("/hello/{name}", func(c *zinc.Context) error {
return c.JSON(zinc.Map{"hello": c.Param("name")})
})
req := httptest.NewRequest(http.MethodGet, "/hello/gopher", nil)
rec := httptest.NewRecorder()
app.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status = %d, body = %s", rec.Code, rec.Body)
}
if got, want := rec.Body.String(), `{"hello":"gopher"}`+"\n"; got != want {
t.Fatalf("body = %q, want %q", got, want)
}
}

Build routes in a function that both main and your tests call. Tests then exercise the real routing, middleware, and error handling.

app.go
func newApp(store Store) *zinc.App {
app := zinc.New()
app.Use(middleware.Recover())
app.Get("/users/{id}", showUser(store))
return app
}
// main.go
func main() {
log.Fatal(newApp(postgresStore()).Listen(":8080"))
}
func TestUsers(t *testing.T) {
app := newApp(fakeStore{"42": {ID: "42", Name: "Ada"}})
tests := []struct {
name, path string
want int
}{
{"found", "/users/42", http.StatusOK},
{"missing", "/users/7", http.StatusNotFound},
{"no such route", "/accounts/42", http.StatusNotFound},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rec := httptest.NewRecorder()
app.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, tt.path, nil))
if rec.Code != tt.want {
t.Fatalf("status = %d, want %d", rec.Code, tt.want)
}
})
}
}
body := strings.NewReader(`{"name":"Ada"}`)
req := httptest.NewRequest(http.MethodPost, "/users", body)
req.Header.Set("Content-Type", "application/json")

Binding picks the decoder from Content-Type, so set it in tests just as a client would.

Mount the middleware on a tiny app and assert what it changes: headers, status, or values it passes on.

func TestRequestID(t *testing.T) {
app := zinc.New()
app.Use(middleware.RequestID())
app.Get("/", func(c *zinc.Context) error { return c.NoContent() })
rec := httptest.NewRecorder()
app.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/", nil))
if rec.Header().Get("X-Request-ID") == "" {
t.Fatal("missing X-Request-ID")
}
}

Use httptest.NewServer when behavior depends on a real client: redirects, cookies, streaming, or HTTP/2.

server := httptest.NewServer(app)
defer server.Close()
res, err := server.Client().Get(server.URL + "/health")
  • Errors explains the status codes your tests will see.
  • Zinc and net/http covers everything else that works because the app is a handler.