Go学习笔记-Testing

Go语言内置了强大的测试框架,支持单元测试、基准测试、示例测试和模糊测试。良好的测试是保证代码质量和可维护性的重要手段。

基本单元测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// math_utils.go
package main

import (
    "errors"
    "math"
)

// 基本数学运算函数
func Add(a, b int) int {
    return a + b
}

func Subtract(a, b int) int {
    return a - b
}

func Multiply(a, b int) int {
    return a * b
}

func Divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

func Factorial(n int) (int, error) {
    if n < 0 {
        return 0, errors.New("factorial of negative number")
    }
    if n == 0 || n == 1 {
        return 1, nil
    }
    
    result := 1
    for i := 2; i <= n; i++ {
        result *= i
    }
    return result, nil
}

func IsPrime(n int) bool {
    if n < 2 {
        return false
    }
    if n == 2 {
        return true
    }
    if n%2 == 0 {
        return false
    }
    
    for i := 3; i <= int(math.Sqrt(float64(n))); i += 2 {
        if n%i == 0 {
            return false
        }
    }
    return true
}

func main() {
    // 主程序逻辑
}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// math_utils_test.go
package main

import (
    "testing"
)

// 1. 基本单元测试
func TestAdd(t *testing.T) {
    result := Add(2, 3)
    expected := 5
    
    if result != expected {
        t.Errorf("Add(2, 3) = %d; expected %d", result, expected)
    }
}

func TestSubtract(t *testing.T) {
    result := Subtract(5, 3)
    expected := 2
    
    if result != expected {
        t.Errorf("Subtract(5, 3) = %d; expected %d", result, expected)
    }
}

// 2. 表格驱动测试
func TestMultiply(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"positive numbers", 3, 4, 12},
        {"negative numbers", -2, -3, 6},
        {"mixed signs", -2, 3, -6},
        {"zero multiplication", 5, 0, 0},
        {"one multiplication", 7, 1, 7},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := Multiply(tt.a, tt.b)
            if result != tt.expected {
                t.Errorf("Multiply(%d, %d) = %d; expected %d", 
                    tt.a, tt.b, result, tt.expected)
            }
        })
    }
}

// 3. 错误处理测试
func TestDivide(t *testing.T) {
    // 正常情况
    result, err := Divide(10, 2)
    if err != nil {
        t.Errorf("Divide(10, 2) returned error: %v", err)
    }
    if result != 5.0 {
        t.Errorf("Divide(10, 2) = %f; expected 5.0", result)
    }
    
    // 除零错误
    _, err = Divide(10, 0)
    if err == nil {
        t.Error("Divide(10, 0) should return error")
    }
    
    expectedError := "division by zero"
    if err.Error() != expectedError {
        t.Errorf("Divide(10, 0) error = %q; expected %q", 
            err.Error(), expectedError)
    }
}

// 4. 子测试
func TestFactorial(t *testing.T) {
    t.Run("positive numbers", func(t *testing.T) {
        tests := []struct {
            input    int
            expected int
        }{
            {0, 1},
            {1, 1},
            {2, 2},
            {3, 6},
            {4, 24},
            {5, 120},
        }
        
        for _, tt := range tests {
            result, err := Factorial(tt.input)
            if err != nil {
                t.Errorf("Factorial(%d) returned error: %v", tt.input, err)
            }
            if result != tt.expected {
                t.Errorf("Factorial(%d) = %d; expected %d", 
                    tt.input, result, tt.expected)
            }
        }
    })
    
    t.Run("negative numbers", func(t *testing.T) {
        _, err := Factorial(-1)
        if err == nil {
            t.Error("Factorial(-1) should return error")
        }
        
        expectedError := "factorial of negative number"
        if err.Error() != expectedError {
            t.Errorf("Factorial(-1) error = %q; expected %q", 
                err.Error(), expectedError)
        }
    })
}

// 5. 测试辅助函数
func TestIsPrime(t *testing.T) {
    // 辅助函数
    assertPrime := func(t *testing.T, n int, expected bool) {
        t.Helper() // 标记为辅助函数
        result := IsPrime(n)
        if result != expected {
            t.Errorf("IsPrime(%d) = %t; expected %t", n, result, expected)
        }
    }
    
    // 测试非质数
    assertPrime(t, 0, false)
    assertPrime(t, 1, false)
    assertPrime(t, 4, false)
    assertPrime(t, 6, false)
    assertPrime(t, 8, false)
    assertPrime(t, 9, false)
    assertPrime(t, 10, false)
    
    // 测试质数
    assertPrime(t, 2, true)
    assertPrime(t, 3, true)
    assertPrime(t, 5, true)
    assertPrime(t, 7, true)
    assertPrime(t, 11, true)
    assertPrime(t, 13, true)
    assertPrime(t, 17, true)
}

// 6. 设置和清理
func TestMain(m *testing.M) {
    // 测试前的设置
    setup()
    
    // 运行测试
    code := m.Run()
    
    // 测试后的清理
    teardown()
    
    // 退出
    os.Exit(code)
}

func setup() {
    // 初始化测试环境
    fmt.Println("Setting up tests...")
}

func teardown() {
    // 清理测试环境
    fmt.Println("Tearing down tests...")
}

基准测试和性能测试

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// benchmark_test.go
package main

import (
    "fmt"
    "math/rand"
    "testing"
    "time"
)

// 1. 基本基准测试
func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(100, 200)
    }
}

func BenchmarkMultiply(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Multiply(123, 456)
    }
}

// 2. 带参数的基准测试
func BenchmarkFactorial(b *testing.B) {
    inputs := []int{5, 10, 15, 20}
    
    for _, input := range inputs {
        b.Run(fmt.Sprintf("input_%d", input), func(b *testing.B) {
            for i := 0; i < b.N; i++ {
                Factorial(input)
            }
        })
    }
}

// 3. 内存分配基准测试
func BenchmarkSliceAppend(b *testing.B) {
    b.Run("without_prealloc", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            var slice []int
            for j := 0; j < 1000; j++ {
                slice = append(slice, j)
            }
        }
    })
    
    b.Run("with_prealloc", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            slice := make([]int, 0, 1000)
            for j := 0; j < 1000; j++ {
                slice = append(slice, j)
            }
        }
    })
}

// 4. 字符串操作基准测试
func BenchmarkStringConcat(b *testing.B) {
    strings := []string{"hello", "world", "go", "programming"}
    
    b.Run("plus_operator", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            var result string
            for _, s := range strings {
                result += s
            }
        }
    })
    
    b.Run("fmt_sprintf", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            result := fmt.Sprintf("%s%s%s%s", 
                strings[0], strings[1], strings[2], strings[3])
            _ = result
        }
    })
    
    b.Run("strings_builder", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            var builder strings.Builder
            for _, s := range strings {
                builder.WriteString(s)
            }
            result := builder.String()
            _ = result
        }
    })
}

// 5. 映射操作基准测试
func BenchmarkMapOperations(b *testing.B) {
    // 准备测试数据
    keys := make([]string, 1000)
    for i := 0; i < 1000; i++ {
        keys[i] = fmt.Sprintf("key_%d", i)
    }
    
    b.Run("map_write", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            m := make(map[string]int)
            for j, key := range keys {
                m[key] = j
            }
        }
    })
    
    b.Run("map_read", func(b *testing.B) {
        // 预先填充映射
        m := make(map[string]int)
        for j, key := range keys {
            m[key] = j
        }
        
        b.ResetTimer() // 重置计时器,不计算准备时间
        
        for i := 0; i < b.N; i++ {
            for _, key := range keys {
                _ = m[key]
            }
        }
    })
}

// 6. 并发基准测试
func BenchmarkConcurrentMap(b *testing.B) {
    m := make(map[string]int)
    var mu sync.RWMutex
    
    b.Run("sequential", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            key := fmt.Sprintf("key_%d", i%100)
            mu.Lock()
            m[key] = i
            mu.Unlock()
        }
    })
    
    b.Run("parallel", func(b *testing.B) {
        b.RunParallel(func(pb *testing.PB) {
            i := 0
            for pb.Next() {
                key := fmt.Sprintf("key_%d", i%100)
                mu.Lock()
                m[key] = i
                mu.Unlock()
                i++
            }
        })
    })
}

// 7. 自定义基准测试指标
func BenchmarkCustomMetrics(b *testing.B) {
    data := make([]int, 1000000)
    for i := range data {
        data[i] = rand.Intn(1000)
    }
    
    b.ResetTimer()
    
    start := time.Now()
    for i := 0; i < b.N; i++ {
        // 模拟排序操作
        bubbleSort(data[:1000]) // 只排序前1000个元素
    }
    elapsed := time.Since(start)
    
    // 报告自定义指标
    b.ReportMetric(float64(b.N*1000)/elapsed.Seconds(), "items/sec")
    b.ReportMetric(float64(elapsed.Nanoseconds())/float64(b.N*1000), "ns/item")
}

func bubbleSort(arr []int) {
    n := len(arr)
    for i := 0; i < n-1; i++ {
        for j := 0; j < n-i-1; j++ {
            if arr[j] > arr[j+1] {
                arr[j], arr[j+1] = arr[j+1], arr[j]
            }
        }
    }
}

示例测试和模糊测试

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// example_test.go
package main

import (
    "fmt"
    "os"
    "strings"
    "sync"
    "testing"
)

// 1. 示例测试
func ExampleAdd() {
    result := Add(2, 3)
    fmt.Println(result)
    // Output: 5
}

func ExampleMultiply() {
    result := Multiply(4, 5)
    fmt.Println(result)
    // Output: 20
}

func ExampleDivide() {
    result, err := Divide(10, 2)
    if err != nil {
        fmt.Printf("Error: %v", err)
        return
    }
    fmt.Printf("%.1f", result)
    // Output: 5.0
}

func ExampleDivide_zero() {
    _, err := Divide(10, 0)
    if err != nil {
        fmt.Printf("Error: %v", err)
    }
    // Output: Error: division by zero
}

func ExampleFactorial() {
    result, _ := Factorial(5)
    fmt.Println(result)
    // Output: 120
}

func ExampleIsPrime() {
    numbers := []int{2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
    for _, n := range numbers {
        if IsPrime(n) {
            fmt.Printf("%d ", n)
        }
    }
    // Output: 2 3 5 7 11
}

// 2. 复杂示例测试
func ExampleStringProcessing() {
    text := "Hello, World! This is a test."
    
    // 转换为大写
    upper := strings.ToUpper(text)
    fmt.Println("Upper:", upper)
    
    // 统计单词数
    words := strings.Fields(text)
    fmt.Println("Word count:", len(words))
    
    // 替换文本
    replaced := strings.ReplaceAll(text, "test", "example")
    fmt.Println("Replaced:", replaced)
    
    // Output:
    // Upper: HELLO, WORLD! THIS IS A TEST.
    // Word count: 6
    // Replaced: Hello, World! This is a example.
}

// 3. 模糊测试 (Go 1.18+)
func FuzzAdd(f *testing.F) {
    // 添加种子语料
    f.Add(1, 2)
    f.Add(-1, -2)
    f.Add(0, 0)
    f.Add(100, -50)
    
    f.Fuzz(func(t *testing.T, a, b int) {
        result := Add(a, b)
        
        // 验证加法的基本性质
        // 交换律: a + b = b + a
        if Add(a, b) != Add(b, a) {
            t.Errorf("Add is not commutative: Add(%d, %d) != Add(%d, %d)", 
                a, b, b, a)
        }
        
        // 恒等元: a + 0 = a
        if Add(a, 0) != a {
            t.Errorf("Add identity failed: Add(%d, 0) != %d", a, a)
        }
        
        // 结果应该等于预期
        expected := a + b
        if result != expected {
            t.Errorf("Add(%d, %d) = %d; expected %d", a, b, result, expected)
        }
    })
}

func FuzzDivide(f *testing.F) {
    // 添加种子语料
    f.Add(10.0, 2.0)
    f.Add(-10.0, 2.0)
    f.Add(10.0, -2.0)
    f.Add(0.0, 1.0)
    
    f.Fuzz(func(t *testing.T, a, b float64) {
        result, err := Divide(a, b)
        
        if b == 0 {
            // 除零应该返回错误
            if err == nil {
                t.Errorf("Divide(%f, 0) should return error", a)
            }
        } else {
            // 正常情况不应该有错误
            if err != nil {
                t.Errorf("Divide(%f, %f) returned unexpected error: %v", 
                    a, b, err)
            }
            
            // 验证结果
            expected := a / b
            if result != expected {
                t.Errorf("Divide(%f, %f) = %f; expected %f", 
                    a, b, result, expected)
            }
        }
    })
}

func FuzzIsPrime(f *testing.F) {
    // 添加已知的质数和合数
    f.Add(2)   // 质数
    f.Add(3)   // 质数
    f.Add(4)   // 合数
    f.Add(17)  // 质数
    f.Add(25)  // 合数
    
    f.Fuzz(func(t *testing.T, n int) {
        // 跳过负数和过大的数
        if n < 0 || n > 1000000 {
            t.Skip("Skipping negative or very large numbers")
        }
        
        result := IsPrime(n)
        
        // 验证一些基本性质
        if n < 2 && result {
            t.Errorf("IsPrime(%d) = true; numbers less than 2 should not be prime", n)
        }
        
        if n == 2 && !result {
            t.Errorf("IsPrime(2) = false; 2 should be prime")
        }
        
        if n > 2 && n%2 == 0 && result {
            t.Errorf("IsPrime(%d) = true; even numbers > 2 should not be prime", n)
        }
        
        // 对于小数字,我们可以验证结果
        if n <= 100 {
            knownPrimes := []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97}
            expected := false
            for _, prime := range knownPrimes {
                if n == prime {
                    expected = true
                    break
                }
            }
            
            if result != expected {
                t.Errorf("IsPrime(%d) = %t; expected %t", n, result, expected)
            }
        }
    })
}

总结

  1. 单元测试

    • 测试函数以Test开头
    • 使用testing.T参数
    • 表格驱动测试提高覆盖率
    • 子测试组织相关测试用例
  2. 基准测试

    • 测试函数以Benchmark开头
    • 使用testing.B参数
    • 循环b.N次执行被测代码
    • 可以测试内存分配和并发性能
  3. 示例测试

    • 测试函数以Example开头
    • 使用// Output:注释验证输出
    • 可以作为文档和测试双重用途
    • 支持多个输出示例
  4. 模糊测试

    • 测试函数以Fuzz开头(Go 1.18+)
    • 使用随机输入发现边界情况
    • 添加种子语料指导测试
    • 验证代码的健壮性
  5. 最佳实践

    • 保持测试简单和独立
    • 使用有意义的测试名称
    • 测试边界条件和错误情况
    • 合理使用测试辅助函数
    • 定期运行测试和基准测试
updatedupdated2025-09-202025-09-20