Skip to content

go test - 测试工具

go test 是 Go 官方提供的测试工具,用于运行单元测试、基准测试和示例代码。

📋 概述

难度级别:⭐⭐⭐
考察范围:测试/代码质量
技术标签单元测试 基准测试 测试覆盖率

问题分析

go test 是 Go 开发中最重要的测试工具,掌握其用法对于编写高质量代码至关重要。

🎯 核心功能

1. 基本用法

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

# 运行指定包的测试
go test ./pkg/...

# 运行所有包的测试
go test ./...

# 运行特定测试函数
go test -run TestFunctionName

# 详细输出
go test -v

# 显示覆盖率
go test -cover

2. 测试文件命名

测试文件必须以 _test.go 结尾:

project/
├── main.go
├── main_test.go      # 测试文件
├── utils.go
└── utils_test.go     # 测试文件

📝 详细示例

示例 1:单元测试

go
package utils

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

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

import "testing"

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

func TestMultiply(t *testing.T) {
    result := Multiply(2, 3)
    if result != 6 {
        t.Errorf("Multiply(2, 3) = %d; want 6", result)
    }
}

运行测试:

bash
$ go test -v
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
=== RUN   TestMultiply
--- PASS: TestMultiply (0.00s)
PASS
ok      example/utils    0.001s

示例 2:表驱动测试

go
package utils

import "testing"

func TestAdd(t *testing.T) {
    tests := []struct {
        name     string
        a        int
        b        int
        expected int
    }{
        {"positive", 2, 3, 5},
        {"negative", -2, -3, -5},
        {"zero", 0, 0, 0},
        {"mixed", -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; want %d",
                    tt.a, tt.b, result, tt.expected)
            }
        })
    }
}

示例 3:基准测试

go
package utils

import "testing"

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

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

运行基准测试:

bash
$ go test -bench=.
goos: darwin
goarch: amd64
BenchmarkAdd-8          1000000000    0.313 ns/op
BenchmarkMultiply-8     1000000000    0.313 ns/op
PASS

示例 4:测试覆盖率

bash
# 显示覆盖率
go test -cover

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

# 查看详细覆盖率
go tool cover -html=coverage.out

🔧 高级用法

1. 测试标志

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

# 运行匹配模式的测试
go test -run TestAdd|TestMultiply

# 并行运行测试
go test -parallel 4

# 超时设置
go test -timeout 30s

# 显示详细输出
go test -v

# 显示测试后的覆盖率
go test -cover

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

# 跳过缓存
go test -count=1

2. 基准测试标志

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

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

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

# 设置运行时间
go test -bench=. -benchtime=10s

# 设置运行次数
go test -bench=. -count=5

3. 测试辅助函数

go
package utils

import (
    "testing"
    "reflect"
)

func TestHelper(t *testing.T) {
    // t.Helper() 标记为辅助函数
    // 错误信息会指向调用者,而不是辅助函数
    helper(t, "test")
}

func helper(t *testing.T, msg string) {
    t.Helper()
    if msg == "" {
        t.Error("msg is empty")
    }
}

4. 测试清理

go
package utils

import "testing"

func TestWithCleanup(t *testing.T) {
    // 设置清理函数
    t.Cleanup(func() {
        // 清理资源
        cleanup()
    })
    
    // 测试代码
    doSomething()
}

🎯 最佳实践

1. 测试组织

go
// 使用子测试组织相关测试
func TestAdd(t *testing.T) {
    t.Run("positive", func(t *testing.T) {
        // 测试正数
    })
    
    t.Run("negative", func(t *testing.T) {
        // 测试负数
    })
}

2. 测试数据

go
// 使用表驱动测试
func TestAdd(t *testing.T) {
    tests := []struct {
        name string
        a, b int
        want int
    }{
        // 测试用例
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            // 测试逻辑
        })
    }
}

3. Mock 和 Stub

go
// 使用接口实现 Mock
type DB interface {
    Get(id int) (string, error)
}

type mockDB struct{}

func (m *mockDB) Get(id int) (string, error) {
    return "mock", nil
}

func TestWithMock(t *testing.T) {
    db := &mockDB{}
    // 使用 mock
}

📊 测试覆盖率

查看覆盖率

bash
# 基本覆盖率
go test -cover

# 详细覆盖率报告
go test -coverprofile=coverage.out
go tool cover -html=coverage.out

# 函数级别覆盖率
go test -coverprofile=coverage.out -covermode=atomic
go tool cover -func=coverage.out

覆盖率目标

  • 最低要求:60%
  • 良好:80%
  • 优秀:90%+

🔍 常见问题

Q1: 如何跳过某些测试?

go
// 使用 build tag
// +build integration

package utils

import "testing"

func TestIntegration(t *testing.T) {
    // 集成测试
}
bash
# 运行集成测试
go test -tags=integration

Q2: 如何测试私有函数?

go
// 方式1:通过公共函数测试
func TestPublic(t *testing.T) {
    // 间接测试私有函数
}

// 方式2:将函数移到测试文件(不推荐)
// 在 _test.go 文件中可以访问同包的私有函数

Q3: 如何测试并发代码?

go
func TestConcurrent(t *testing.T) {
    // 使用 race detector
    // go test -race
    
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            // 测试代码
        }()
    }
    wg.Wait()
}

📖 参考资源

正在精进