在 Go 语言中,编写单元测试是通过标准库中的 testing 包实现的。

一、基本结构

  1. 测试文件命名
    测试文件必须以 _test.go 结尾。
    测试函数必须以 Test 开头,后跟大写字母,例如 TestAdd。
    测试函数接受一个 *testing.T 参数。
  2. 示例:简单加法测试
    1
    2
    3
    4
    5
    6
    // add.go
    package main

    func Add(a, b int) int {
    return a + b
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // add_test.go
    package main

    import "testing"

    func TestAdd(t *testing.T) {
    result := Add(2, 3)
    expected := 5

    if result != expected {
    t.Errorf("Add(2,3) = %d; expected %d", result, expected)
    }
    }

二、表格驱动测试(Table-driven Tests)

这是 Go 中推荐的测试方式,使用结构体数组组织多个测试用例,便于维护。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func TestAdd(t *testing.T) {
testCases := []struct {
name string
a, b int
expected int
}{
{"positive numbers", 2, 3, 5},
{"negative numbers", -1, -1, -2},
{"zero", 0, 0, 0},
{"mixed", -1, 1, 0},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := Add(tc.a, tc.b)
if result != tc.expected {
t.Errorf("%s: Add(%d, %d) = %d; expected %d", tc.name, tc.a, tc.b, result, tc.expected)
}
})
}
}

三、断言方式

Go 标准库没有内置的断言函数,通常手动使用 if 判断并调用 t.Error 或 t.Fatalf。
推荐断言库(可选):
stretch/testify 提供了更丰富的断言方式,如 assert.Equal、require.NoError 等,提高可读性。

1
2
3
4
5
import "github.com/stretchr/testify/assert"

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

四、性能测试(Benchmark)

使用 Benchmark 函数进行性能测试,Go 提供了内置支持。

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

运行命令:

1
go test -bench=.

五、测试覆盖率(Coverage)

Go 支持测试覆盖率分析,可查看代码覆盖情况。

1
go test -cover

生成 HTML 报告:

1
2
go test -coverprofile=coverage.out
go tool cover -html=coverage.out

六、常用测试命令

命令 说明
go test 运行当前目录下所有测试
go test -v 显示详细输出信息
go test -run TestName 只运行指定测试函数
go test -bench=. 运行所有性能测试
go test -cover 显示测试覆盖率
go test -coverprofile=coverage.out 生成覆盖率文件
go tool cover -html=coverage.out 生成 HTML 覆盖率报告

七、测试规范与最佳实践

测试与代码在同一目录,文件名以 _test.go 结尾。
保持测试独立:每个测试应独立运行,不依赖其他测试状态。
使用子测试(t.Run):提升可读性和组织能力。
清理资源:使用 defer 清理临时文件、数据库连接等。
并行测试:使用 t.Parallel() 提升测试速度(适用于无副作用的测试)。
测试边界情况:考虑输入边界、非法值、空值等。
合理使用测试辅助函数:避免重复代码。

八、TestMain(可选)

如果你需要在所有测试运行前或后执行初始化/清理操作,可以定义TestMain 函数。

1
2
3
4
5
6
func TestMain(m *testing.M) {
fmt.Println("Before all tests")
exitCode := testing.MainStart(m)
fmt.Println("After all tests")
os.Exit(exitCode)
}

注意:Go 1.15 之后,testing.MainStart 已弃用,建议使用 m.Run() 替代。