golangtutorialAll tutorials

Testing in Go: Table Tests, HTTP, Fakes, and Coverage

Write effective Go tests with table-driven cases, subtests, httptest, fakes, race detection, benchmarks, and useful coverage.

Testing · Lesson 1Saved in this browser. No account required.

Go includes testing, benchmarking, fuzzing, and coverage support in its standard toolchain. Tests live in files ending with _test.go and run with go test ./....

Start with behavior

func TestAdd(t *testing.T) {
	got := Add(2, 3)
	if got != 5 {
		t.Fatalf("Add(2, 3) = %d; want 5", got)
	}
}

Failure messages should show the operation, actual result, and expected result.

Use table-driven tests

func TestNormalize(t *testing.T) {
	tests := []struct {
		name string
		in   string
		want string
	}{
		{"trims", "  Go ", "go"},
		{"empty", "", ""},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := Normalize(tt.in); got != tt.want {
				t.Fatalf("got %q; want %q", got, tt.want)
			}
		})
	}
}

Named subtests make failures easy to locate. Add cases that represent meaningful behavior boundaries, not every imaginable value.

Test HTTP handlers

req := httptest.NewRequest(http.MethodGet, "/health", nil)
rec := httptest.NewRecorder()

handler.ServeHTTP(rec, req)

if rec.Code != http.StatusOK {
	t.Fatalf("status = %d; want %d", rec.Code, http.StatusOK)
}

Assert status, important headers, and decoded response data. Avoid brittle comparison of irrelevant JSON formatting.

Replace external boundaries with fakes

Define a small consumer-owned interface, then provide a fake repository or client with explicit results. This keeps unit tests deterministic without abstracting every internal type.

Run race, coverage, and fuzz checks

go test -race ./...
go test -cover ./...
go test -fuzz=FuzzParse ./...

Coverage finds unexecuted code; it does not measure assertion quality. Use it to discover gaps, not as the sole definition of correctness.

Benchmark after correctness

func BenchmarkParse(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Parse(sample)
	}
}

Run benchmarks in a stable environment and compare results statistically. Optimize only costs that matter in representative workloads.

A maintainable test suite is fast, deterministic, behavior-focused, and strongest at boundaries where mistakes are expensive.