Skip to content

测试和性能

Go 语言内置了强大的测试框架,支持单元测试、基准测试和性能分析。

单元测试

基本测试

测试文件以 _test.go 结尾,测试函数以 Test 开头:

go
// math.go
package math

func Add(a, b int) int {
    return a + b
}

// math_test.go
package math

import "testing"

func TestAdd(t *testing.T) {
    result := Add(2, 3)
    if result != 5 {
        t.Errorf("Add(2, 3) = %d; 期望 5", result)
    }
}

运行测试

bash
# 运行当前包的所有测试
go test

# 运行特定测试
go test -run TestAdd

# 显示详细输出
go test -v

# 显示覆盖率
go test -cover

表驱动测试

go
func TestAdd(t *testing.T) {
    tests := []struct {
        name     string
        a        int
        b        int
        expected int
    }{
        {"正数", 2, 3, 5},
        {"负数", -2, -3, -5},
        {"零", 0, 5, 5},
        {"混合", -2, 3, 1},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := Add(tt.a, tt.b)
            if result != tt.expected {
                t.Errorf("Add(%d, %d) = %d; 期望 %d", 
                    tt.a, tt.b, result, tt.expected)
            }
        })
    }
}

测试辅助函数

go
func assertEqual(t *testing.T, got, want int) {
    t.Helper()  // 标记为辅助函数
    if got != want {
        t.Errorf("得到 %d, 期望 %d", got, want)
    }
}

func TestAdd(t *testing.T) {
    assertEqual(t, Add(2, 3), 5)
}

测试设置和清理

go
func setup() {
    // 测试前设置
}

func teardown() {
    // 测试后清理
}

func TestMain(m *testing.M) {
    setup()
    code := m.Run()
    teardown()
    os.Exit(code)
}

基准测试

基准测试函数以 Benchmark 开头:

go
func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(1, 2)
    }
}

运行基准测试

bash
# 运行基准测试
go test -bench=.

# 运行特定基准测试
go test -bench=BenchmarkAdd

# 显示内存分配
go test -bench=. -benchmem

# 比较基准测试
go test -bench=. -benchmem > old.txt
# 修改代码后
go test -bench=. -benchmem > new.txt
benchcmp old.txt new.txt

基准测试示例

go
func BenchmarkStringConcat(b *testing.B) {
    var result string
    for i := 0; i < b.N; i++ {
        result += "a"
    }
}

func BenchmarkStringBuilder(b *testing.B) {
    var builder strings.Builder
    for i := 0; i < b.N; i++ {
        builder.WriteString("a")
    }
    _ = builder.String()
}

示例测试

示例测试以 Example 开头,用于文档:

go
func ExampleAdd() {
    result := Add(2, 3)
    fmt.Println(result)
    // Output: 5
}

性能分析

CPU 性能分析

go
import (
    "os"
    "runtime/pprof"
)

func main() {
    f, _ := os.Create("cpu.prof")
    defer f.Close()
    
    pprof.StartCPUProfile(f)
    defer pprof.StopCPUProfile()
    
    // 你的代码
}

内存性能分析

go
import "runtime/pprof"

func main() {
    f, _ := os.Create("mem.prof")
    defer f.Close()
    
    // 你的代码
    
    pprof.WriteHeapProfile(f)
}

使用 go test 进行性能分析

bash
# CPU 性能分析
go test -cpuprofile=cpu.prof -bench=.

# 内存性能分析
go test -memprofile=mem.prof -bench=.

# 查看性能分析结果
go tool pprof cpu.prof

代码覆盖率

生成覆盖率报告

bash
# 生成覆盖率文件
go test -coverprofile=coverage.out

# 查看覆盖率
go tool cover -func=coverage.out

# 生成 HTML 报告
go tool cover -html=coverage.out

覆盖率示例

go
func TestCoverage(t *testing.T) {
    // 测试各种情况以提高覆盖率
    Add(1, 2)
    Add(-1, -2)
    Add(0, 0)
}

测试工具

testify

第三方测试库,提供更多断言功能:

go
import (
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
)

func TestAdd(t *testing.T) {
    result := Add(2, 3)
    assert.Equal(t, 5, result)
    require.NotNil(t, result)
}

httptest

HTTP 测试工具:

go
import (
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestHandler(t *testing.T) {
    req := httptest.NewRequest("GET", "/", nil)
    w := httptest.NewRecorder()
    
    handler(w, req)
    
    if w.Code != http.StatusOK {
        t.Errorf("期望状态码 %d, 得到 %d", http.StatusOK, w.Code)
    }
}

完整示例

go
// math.go
package math

func Add(a, b int) int {
    return a + b
}

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

// math_test.go
package math

import (
    "testing"
)

func TestAdd(t *testing.T) {
    tests := []struct {
        name     string
        a        int
        b        int
        expected int
    }{
        {"正数", 2, 3, 5},
        {"负数", -2, -3, -5},
        {"零", 0, 5, 5},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := Add(tt.a, tt.b)
            if result != tt.expected {
                t.Errorf("Add(%d, %d) = %d; 期望 %d", 
                    tt.a, tt.b, result, tt.expected)
            }
        })
    }
}

func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(1, 2)
    }
}

func ExampleAdd() {
    result := Add(2, 3)
    fmt.Println(result)
    // Output: 5
}

最佳实践

  1. 测试应该快速:避免慢速测试影响开发效率
  2. 测试应该独立:每个测试不应该依赖其他测试
  3. 使用表驱动测试:提高测试覆盖率和可维护性
  4. 测试边界情况:包括正常情况、边界情况和错误情况
  5. 保持测试简单:测试代码应该易于理解和维护
  6. 定期运行基准测试:确保性能不会退化
  7. 关注代码覆盖率:但不要盲目追求 100% 覆盖率

下一步

接下来我们将学习:

基于 VitePress 构建