1. Go 测试基础与 testing 包概览
1.1 testing 包的定位与核心对象
本文聚焦 Go语言测试入门:testing 包的使用详解与实战案例 的核心要点。testing 包 是 Go 标准库中的测试框架,负责组织、执行和报告测试。核心对象包括 testing.T、testing.B 和 testing.M,分别用于单元测试、基准测试和入口控制。
通过 TestXxx 命名的函数会被自动识别为测试用例,BenchmarkXxx 用于基准测试,而 TestMain(m *testing.M) 提供全局初始化与清理的入口。
测试运行器会汇总测试结果,t.Fail、t.Fatalf、t.Skip 等 API 提供不同的失败策略和跳过机制。
package example
import "testing"
func Sum(a, b int) int { return a + b }
func TestSum(t *testing.T) {
if Sum(2, 3) != 5 {
t.Fatalf("Sum(2,3) = %d; want 5", Sum(2, 3))
}
}
1.2 测试与包结构的组织原则
测试文件命名以 _test.go 结尾,文件内容放在与被测试代码同包名的目录下。包名可以是 xxx 或 xxx_test,后者有利于实现黑盒测试和更严格的 API 粒度。
在实际项目中,保持测试代码与生产代码的分离是关键,目录结构应与模块结构对齐,方便 CI 运行。多个测试文件可以共享辅助函数或测试数据。
1.3 测试文件与示例代码组织
// 文件:math/math.go
package math
func Add(a, b int) int { return a + b }
// 文件:math/math_test.go
package math
import "testing"
func TestAdd(t *testing.T) {
if got := Add(2, 3); got != 5 {
t.Fatalf("Add(2,3) = %d; want 5", got)
}
}
2. 编写你的第一个测试用例
2.1 测试文件结构与初步示例
要开始 Go 测试,首先确认你的项目包含测试文件以 _test.go 结尾。测试文件可以与被测试的源码在同一包中,也可以在独立的测试包中进行黑盒测试。
下面给出一个简单示例,演示如何为一个加法函数编写基本测试。测试函数以 Test 前缀命名,并接收 *testing.T 参数。
// 文件:math/add.go
package math
func Add(a, b int) int { return a + b }
// 文件:math/add_test.go
package math
import "testing"
func TestAdd(t *testing.T) {
if got := Add(2, 3); got != 5 {
t.Fatalf("Add(2,3) = %d; want 5", got)
}
}
2.2 使用 t.Fatalf 提供清晰的断言信息
在断言失败时,t.Fatalf 可以输出详细信息并立即停止当前测试,确保失败原因可追溯。正确的断言信息有助于定位逻辑错误和边界条件。
你也可以使用 t.Errorf 输出错误但继续执行其他子测试,这在表格驱动测试中尤其有用。
package math
import "testing"
func TestAddEdge(t *testing.T) {
cases := []struct{ a, b, want int }{
{0, 0, 0},
{1, -1, 0},
{100, 200, 300},
}
for _, c := range cases {
if got := Add(c.a, c.b); got != c.want {
t.Errorf("Add(%d,%d) = %d; want %d", c.a, c.b, got, c.want)
}
}
}
3. 使用 testing 包的高级特性
3.1 子测试与 t.Run 的用法
通过 t.Run 可以在同一个测试函数中创建多个子测试,便于隔离测试用例、单独统计失败情况,并提升并行测试的可控性。
使用子测试时,t.Parallel 可以促进并行执行,但需要避免并发访问共享数据。
package math
import "testing"
func TestDivide(t *testing.T) {
t.Run("normal", func(t *testing.T) {
if 10/2 != 5 {
t.Fatalf("unexpected result")
}
})
t.Run("division by zero", func(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Fatalf("expected panic on divide by zero")
}
}()
_ = 1 / 0
})
}
3.2 表格驱动测试的实践
表格驱动测试将输入输出对整理成数据结构,循环遍历进行断言,提高代码可维护性和覆盖率。
下面示例展示了一个典型的表格驱动测试结构。
package math
import "testing"
func Add(a, b int) int { return a + b }
func TestAdd_TableDriven(t *testing.T) {
cases := []struct {
a, b, want int
}{
{1, 2, 3},
{2, 2, 4},
{0, 0, 0},
}
for _, c := range cases {
if got := Add(c.a, c.b); got != c.want {
t.Fatalf("Add(%d,%d) = %d; want %d", c.a, c.b, got, c.want)
}
}
}
4. 实战案例:结合基准测试与覆盖率
4.1 基准测试示例
基准测试(Benchmark)用于评估代码的性能,并帮助定位瓶颈。Go 的基准测试以 BenchmarkXxx 命名,参数为 *testing.B。
在基准测试中,循环次数由系统根据 b.N 自动调整,确保测量的可重复性与统计稳定性。
package math
import "testing"
func Add(a, b int) int { return a + b }
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = Add(1, 2)
}
}
4.2 测试覆盖率与持续集成集成
通过覆盖率可以了解代码哪些部分被测试覆盖,go test -cover 命令输出覆盖率百分比,结合持续集成工具可以持续提升覆盖率。
go test ./... -cover


