Skip to content
Skip to content
DocsGo LearningexpertTesting
Chapter 12 of 14·expert·10 min read

Testing

Kiểm Thử

Unit tests, table-driven tests, and benchmarks

Hover or tap any paragraph to see Vietnamese translation

Testing in Go

Go has built-in testing support via the testing package. No external framework needed -> just write test functions with signature func TestXxx(t *testing.T) and go test automatically finds and runs them. This encourages writing tests alongside code.

Basic Test Functions

Test functions must start with Test (capitalized), take *testing.T, and live in *_test.go files. Use t.Error, t.Errorf, t.Fatal to report failures. t.Fatal stops the test immediately; t.Error continues.

math_test.go
1// math.go2package math34func Add(a, b int) int {5	return a + b6}78func Divide(a, b float64) (float64, error) {9	if b == 0 {10		return 0, fmt.Errorf("division by zero")11	}12	return a / b, nil
Tip
Test functions should not print results -> use t.Log and t.Logf for debugging if needed. Only use t.Error/t.Fatal to report test results.

Table-Driven Tests

Table-driven tests are idiomatic in Go. Create a slice of structs containing input, expected output, and description. Loop through each test case with t.Run() to create a subtest. This allows testing many inputs without repeating test code.

math_test.go
1package math23import "testing"45func TestAddTableDriven(t *testing.T) {6	tests := []struct {7		name     string8		a, b     int9		expected int10	}{11		{"positive numbers", 2, 3, 5},12		{"negative numbers", -1, -2, -3},

Subtests with t.Run

t.Run(name, func) creates a subtest. Subtests are useful because: (1) you can run specific subtests with go test -run TestName/SubtestName, (2) failures report the subtest name clearly, (3) you can split complex test logic into smaller pieces.

math_test.go
1package math23import "testing"45func TestAddDetailed(t *testing.T) {6	t.Run("positive numbers", func(t *testing.T) {7		if Add(2, 3) != 5 {8			t.Error("failed")9		}10	})1112	t.Run("negative numbers", func(t *testing.T) {

Benchmarks

Benchmark functions have signature func BenchmarkXxx(b *testing.B), live in *_test.go files. Loop over b.N (iterations determined by the framework) and run the code you want to benchmark. go test -bench=. runs benchmarks. -benchmem shows allocations.

math_test.go
1package math23import "testing"45func BenchmarkAdd(b *testing.B) {6	for i := 0; i < b.N; i++ {7		Add(2, 3)8	}9}1011func BenchmarkDivide(b *testing.B) {12	for i := 0; i < b.N; i++ {13		Divide(10, 2)14	}15}1617// Run with: go test -bench=. -benchmem18// Output:19// BenchmarkAdd-8        1000000000  1.234 ns/op  0 B/op  0 allocs/op20// BenchmarkDivide-8     100000000   10.56 ns/op  0 B/op  0 allocs/op

Test Helper Functions

t.Helper() marks a function as a helper. This makes stack traces point to the test that called the helper, not the helper itself. Useful when you have common setup logic or assertions.

math_test.go
1package math23import "testing"45// Helper function for comparing results6func assertEqual(t *testing.T, got, want int, msg string) {7	t.Helper()  // Mark as helper so errors point to caller8	if got != want {9		t.Errorf("%s: got %d, want %d", msg, got, want)10	}11}1213func TestAddWithHelper(t *testing.T) {14	assertEqual(t, Add(2, 3), 5, "Add(2, 3)")15	assertEqual(t, Add(-1, 1), 0, "Add(-1, 1)")16	assertEqual(t, Add(0, 0), 0, "Add(0, 0)")17}1819// Error stack trace points to assertEqual call in test,20// not to the comparison inside assertEqual

go test Flags

Common flags: -v (verbose output), -run pattern (run tests matching pattern), -race (detect data races), -cover (code coverage %), -coverprofile file (save coverage data).

Terminal
1// Running tests2go test ./...                      // test all packages3go test -v ./...                   // verbose output4go test -run TestAdd              // run TestAdd* tests5go test -run TestAdd/positive     // run TestAdd/positive subtest6go test -race ./...                // detect data races78// Coverage9go test -cover ./...               // show coverage percentage10go test -coverprofile=coverage.out ./...11go tool cover -html=coverage.out   // open HTML coverage report1213// Benchmark14go test -bench=.                   // run all benchmarks15go test -bench=. -benchmem         // show memory allocations16go test -bench=. -benchtime=10s    // run for 10 seconds

Example Functions

Example functions have signature func ExampleXxx() and are run as tests. Expected output goes in // Output: comment. If no output, the example still runs but is not checked. Examples serve as both tests and documentation.

math_test.go
1package math23// ExampleAdd demonstrates the Add function4func ExampleAdd() {5	result := Add(2, 3)6	fmt.Println(result)7	// Output: 58}910// ExampleAdd_negative shows Add with negative numbers11func ExampleAdd_negative() {12	result := Add(-1, -2)13	fmt.Println(result)14	// Output: -315}1617// Examples appear in godoc and can be run with 'go test'18// go test -run Example

Setup and Teardown with TestMain

func TestMain(m *testing.M) lets you run setup before and teardown after all tests. Call m.Run() to run tests, then exit with the status code it returns.

math_test.go
1package math23import (4	"fmt"5	"testing"6)78func TestMain(m *testing.M) {9	// Setup: runs before all tests10	fmt.Println("Setting up test environment...")11	setupDatabase()12

Key Takeaways

Điểm Chính

  • func TestXxx(t *testing.T) is the signature for test functionsfunc TestXxx(t *testing.T) là signature cho hàm test
  • Table-driven tests are idiomatic Go for testing multiple inputsTable-driven test là idiomatic Go để kiểm tra nhiều input
  • t.Run creates subtests that can be run individually with -run flagt.Run tạo subtest có thể chạy riêng lẻ bằng flag -run
  • go test -bench=. -benchmem runs performance tests and shows memory allocationsgo test -bench=. -benchmem chạy test hiệu suất và hiển thị cấp phát bộ nhớ

Practice

Test your understanding of this chapter

Quiz

What does t.Fatal() do differently from t.Error()?

t.Fatal() khác với t.Error() như thế nào?

Quiz

When using table-driven tests with t.Run(), what is the main benefit?

Khi sử dụng table-driven test với t.Run(), lợi ích chính là gì?

True or False

Benchmark functions must start with Benchmark (capitalized) and take *testing.B as a parameter.

Hàm benchmark phải bắt đầu bằng Benchmark (viết hoa) và nhận *testing.B làm tham số.

True or False

ExampleAdd() functions are only for documentation; they do not run as tests.

Các hàm ExampleAdd() chỉ dành cho tài liệu; chúng không chạy như test.

Code Challenge

Fill in the t.Run() call to create a subtest

Điền vào lệnh gọi t.Run() để tạo subtest

t.("addition", func(t *testing.T) {
  if Add(2, 3) != 5 {
    t.Error("failed")
  }
})
Built: 6/25/2026, 3:03:36 PM