Search and navigate

Pages

Start HereRoadmapTutorialsCheatsheetInterview QuestionsResources

Published tutorials

Building a REST API with Go: The Complete TutorialGeneric Functions in Go: Writing Reusable CodeGo Benchmarking: Measuring Performance with testing.BGo Channels: Communication Between GoroutinesGo Constraints: comparable, Ordered and Custom SetsGo Context: Cancellation, Deadlines, and Request ValuesGo HTTP Client: Timeouts, JSON, and Reliable RequestsGo HTTP Middleware: Build a Production ChainGo Interview Questions: 25 Answers That Show UnderstandingGo Mutex: Protecting Shared State from Race ConditionsGo net/http: Build an HTTP Server from ScratchGo select Statement: Multiplexing Channels ExplainedGo Tools You Need for a Reliable Development LoopGo Tutorial: From First Program to Backend FoundationsGo Type Parameters: Syntax and Constraints ExplainedGo Worker Pool Pattern: Bounded Concurrency at ScaleGo's Type System Deep Dive: Static Typing Done RightGolang Compilation and Execution ExplainedGolang File Structure for Modules and ApplicationsGolang Hello World and Your First Go ProgramGolang IDEs: Choose an Editor for Your WorkflowGolang Interfaces: What They Are and How to Use ThemGoroutine Leaks in Go: How to Detect and Prevent ThemGoroutines in Go: Concurrency Without LeaksHow to Download and Install Golang SafelyHow to Learn Golang with a Focused Study PlanIntegration Testing in Go: Testing Real BoundariesJSON in Go: Encoding, Decoding, and ValidationLearn Golang with a Practical Beginner CourseMocking in Go: Test Doubles Without Magicsync.WaitGroup in Go: Coordinating Multiple GoroutinesTable-Driven Tests in Go: The Idiomatic WayTesting in Go: Table Tests, HTTP, Fakes, and CoverageWhat Is Go and How Does a Go Program Work?Why Learn Golang and What Is Go Used For?Writing Unit Tests in Go: A Practical Guide
Open full search and filters →

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.

Standard-library lessonGo requirement: Go 1.21+How tutorials are checked
Testing · Lesson 1Saved in this browser. No account required.

Verification (10 August 2026): Self-contained runnable examples were compile-tested with Go 1.26.5 on Windows/amd64. Multi-file or contextual snippets and intentional compiler-error demonstrations were checked separately in the repository verification matrix.

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.

Use many narrow handler tests, focused httptest.Server integration tests, and a small critical set of end-to-end checks.
Comparison of handler-only, httptest server, and end-to-end HTTP test boundaries, including what each catches and its tradeoffs

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.