Go 语言测试(go test)使用说明

Go 语言内置了强大的测试框架,通过 go test 命令可以方便地进行单元测试、基准测试和示例测试。

基本概念

  • 测试文件以 _test.go 结尾
  • 测试函数以 Test 开头,参数为 *testing.T
  • 基准测试函数以 Benchmark 开头,参数为 *testing.B
  • 示例函数以 Example 开头,无参数

常用命令

1go test                    # 运行当前目录下的所有测试
2go test -v                 # 显示详细输出
3go test -run TestName      # 运行指定的测试函数
4go test ./...              # 递归测试所有子目录
5go test -cover             # 显示测试覆盖率
6go test -bench .           # 运行基准测试
7go test -timeout 30s       # 设置测试超时时间

完整示例

下面提供一个包含单元测试、表格驱动测试、基准测试和示例测试的完整案例:

calculator.go
 1package calculator
 2
 3import (
 4	"errors"
 5	"math"
 6)
 7
 8// Add 加法运算
 9func Add(a, b int) int {
10	return a + b
11}
12
13// Subtract 减法运算
14func Subtract(a, b int) int {
15	return a - b
16}
17
18// Multiply 乘法运算
19func Multiply(a, b int) int {
20	return a * b
21}
22
23// Divide 除法运算
24func Divide(a, b float64) (float64, error) {
25	if b == 0 {
26		return 0, errors.New("division by zero")
27	}
28	return a / b, nil
29}
30
31// IsPrime 判断是否为质数
32func IsPrime(n int) bool {
33	if n <= 1 {
34		return false
35	}
36	if n <= 3 {
37		return true
38	}
39	if n%2 == 0 || n%3 == 0 {
40		return false
41	}
42	for i := 5; i*i <= n; i += 6 {
43		if n%i == 0 || n%(i+2) == 0 {
44			return false
45		}
46	}
47	return true
48}
49
50// Factorial 计算阶乘
51func Factorial(n int) int {
52	if n < 0 {
53		return -1
54	}
55	if n == 0 {
56		return 1
57	}
58	result := 1
59	for i := 1; i <= n; i++ {
60		result *= i
61	}
62	return result
63}
64
65// Sqrt 计算平方根
66func Sqrt(x float64) (float64, error) {
67	if x < 0 {
68		return 0, errors.New("negative number")
69	}
70	return math.Sqrt(x), nil
71}
72
calculator_test.go
  1package calculator
  2
  3import (
  4	"fmt"
  5	"testing"
  6)
  7
  8// TestAdd 基本的单元测试
  9func TestAdd(t *testing.T) {
 10	result := Add(2, 3)
 11	expected := 5
 12	if result != expected {
 13		t.Errorf("Add(2, 3) = %d; want %d", result, expected)
 14	}
 15}
 16
 17// TestSubtract 使用子测试
 18func TestSubtract(t *testing.T) {
 19	t.Run("positive numbers", func(t *testing.T) {
 20		result := Subtract(10, 5)
 21		if result != 5 {
 22			t.Errorf("got %d, want 5", result)
 23		}
 24	})
 25
 26	t.Run("negative result", func(t *testing.T) {
 27		result := Subtract(5, 10)
 28		if result != -5 {
 29			t.Errorf("got %d, want -5", result)
 30		}
 31	})
 32}
 33
 34// TestDivide 测试带错误返回的函数
 35func TestDivide(t *testing.T) {
 36	// 正常情况
 37	result, err := Divide(10, 2)
 38	if err != nil {
 39		t.Fatalf("unexpected error: %v", err)
 40	}
 41	if result != 5.0 {
 42		t.Errorf("Divide(10, 2) = %f; want 5.0", result)
 43	}
 44
 45	// 除零错误
 46	_, err = Divide(10, 0)
 47	if err == nil {
 48		t.Error("expected error for division by zero, got nil")
 49	}
 50}
 51
 52// TestIsPrime 表格驱动测试(推荐方式)
 53func TestIsPrime(t *testing.T) {
 54	testCases := []struct {
 55		name     string
 56		input    int
 57		expected bool
 58	}{
 59		{"prime 2", 2, true},
 60		{"prime 3", 3, true},
 61		{"prime 5", 5, true},
 62		{"prime 7", 7, true},
 63		{"prime 11", 11, true},
 64		{"not prime 1", 1, false},
 65		{"not prime 4", 4, false},
 66		{"not prime 6", 6, false},
 67		{"not prime 8", 8, false},
 68		{"not prime 9", 9, false},
 69		{"negative", -5, false},
 70	}
 71
 72	for _, tc := range testCases {
 73		t.Run(tc.name, func(t *testing.T) {
 74			result := IsPrime(tc.input)
 75			if result != tc.expected {
 76				t.Errorf("IsPrime(%d) = %v; want %v", tc.input, result, tc.expected)
 77			}
 78		})
 79	}
 80}
 81
 82// TestFactorial 表格驱动测试
 83func TestFactorial(t *testing.T) {
 84	tests := []struct {
 85		input    int
 86		expected int
 87	}{
 88		{0, 1},
 89		{1, 1},
 90		{5, 120},
 91		{10, 3628800},
 92		{-1, -1}, // 错误情况
 93	}
 94
 95	for _, tt := range tests {
 96		t.Run(fmt.Sprintf("factorial_%d", tt.input), func(t *testing.T) {
 97			result := Factorial(tt.input)
 98			if result != tt.expected {
 99				t.Errorf("Factorial(%d) = %d; want %d", tt.input, result, tt.expected)
100			}
101		})
102	}
103}
104
105// TestSqrt 测试浮点数
106func TestSqrt(t *testing.T) {
107	result, err := Sqrt(16)
108	if err != nil {
109		t.Fatalf("unexpected error: %v", err)
110	}
111	if result != 4.0 {
112		t.Errorf("Sqrt(16) = %f; want 4.0", result)
113	}
114
115	// 测试负数
116	_, err = Sqrt(-1)
117	if err == nil {
118		t.Error("expected error for negative input")
119	}
120}
121
122// BenchmarkAdd 基准测试
123func BenchmarkAdd(b *testing.B) {
124	for i := 0; i < b.N; i++ {
125		Add(100, 200)
126	}
127}
128
129// BenchmarkIsPrime 基准测试
130func BenchmarkIsPrime(b *testing.B) {
131	for i := 0; i < b.N; i++ {
132		IsPrime(97)
133	}
134}
135
136// BenchmarkFactorial 基准测试
137func BenchmarkFactorial(b *testing.B) {
138	for i := 0; i < b.N; i++ {
139		Factorial(10)
140	}
141}
142
143// ExampleAdd 示例函数
144func ExampleAdd() {
145	result := Add(2, 3)
146	fmt.Println(result)
147	// Output: 5
148}
149
150// ExampleDivide 示例函数
151func ExampleDivide() {
152	result, _ := Divide(10, 2)
153	fmt.Println(result)
154	// Output: 5
155}
156
157// ExampleIsPrime 示例函数
158func ExampleIsPrime() {
159	fmt.Println(IsPrime(7))
160	fmt.Println(IsPrime(8))
161	// Output:
162	// true
163	// false
164}
165
166// TestMain 测试主函数(可选)
167// 可以在所有测试前后执行设置和清理工作
168func TestMain(m *testing.M) {
169	// 测试前的设置
170	fmt.Println("Setting up tests...")
171	
172	// 运行测试
173	code := m.Run()
174	
175	// 测试后的清理
176	fmt.Println("Cleaning up after tests...")
177	
178	// 退出
179	// os.Exit(code)
180	_ = code
181}
182

运行测试的命令示例

 1# 基本测试
 2go test
 3
 4# 详细输出
 5go test -v
 6
 7# 运行特定测试
 8go test -run TestAdd
 9go test -run TestIsPrime
10
11# 运行包含特定名称的测试
12go test -run Prime
13
14# 测试覆盖率
15go test -cover
16go test -coverprofile=coverage.out
17go tool cover -html=coverage.out  # 生成HTML覆盖率报告
18
19# 运行基准测试
20go test -bench .
21go test -bench BenchmarkAdd
22go test -benchmem  # 显示内存分配统计
23
24# 并行测试
25go test -parallel 4
26
27# 设置超时
28go test -timeout 10s

测试最佳实践

  1. 使用表格驱动测试:可以用更少的代码测试更多场景
  2. 使用子测试:通过 t.Run() 组织相关测试
  3. 测试错误情况:不仅测试正常流程,也要测试异常情况
  4. 使用有意义的测试名称:便于识别失败的测试
  5. 避免测试间依赖:每个测试应该独立运行
  6. 使用 t.Helper():标记辅助函数,使错误报告更准确
  7. 合理使用 Mock:对于复杂依赖,使用接口和 Mock 简化测试

这些示例涵盖了 Go 测试的主要用法,可以根据实际需求进行调整和扩展。