测试框架基础
Go语言内置了轻量级测试框架testing
,无需第三方依赖即可完成基础单元测试。测试文件需以_test.go
结尾,测试函数遵循TestXxx
命名规范,其中Xxx
首字母必须大写。
package calculator
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Expected 5, got %d", result)
}
}
测试覆盖率是衡量测试完整性的重要指标,可通过go test -cover
获取。行业实践表明,核心逻辑应达到80%以上的覆盖率,但不应盲目追求数值,需结合业务关键性评估。
高级测试技术
表格驱动测试
当测试相同逻辑的不同输入输出组合时,表格驱动测试能显著减少重复代码:
func TestMultiply(t *testing.T) {
cases := []struct {
name string
a, b int
expected int
}{
{"positive", 2, 3, 6},
{"negative", -1, 5, -5},
{"zero", 0, 10, 0},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if actual := Multiply(tc.a, tc.b); actual != tc.expected {
t.Errorf("%s: expected %d, got %d", tc.name, tc.expected, actual)
}
})
}
}
优势包括:
– 测试用例集中管理
– 新增用例只需添加数据行
– 子测试独立执行和报告
模拟与桩测试
对于依赖外部资源的代码,可使用接口抽象配合模拟实现:
type DB interface {
GetUser(id int) (*User, error)
}
func ProcessUser(db DB, id int) error {
user, err := db.GetUser(id)
// 处理逻辑
}
// 测试用模拟实现
type mockDB struct{}
func (m *mockDB) GetUser(id int) (*User, error) {
return &User{ID: id, Name: "test"}, nil
}
func TestProcessUser(t *testing.T) {
db := &mockDB{}
err := ProcessUser(db, 1)
if err != nil {
t.Fatal(err)
}
}
并发测试
Go的并发特性需要特殊测试策略。testing
包提供Parallel()
标记并行测试:
func TestConcurrentMap(t *testing.T) {
m := sync.Map{}
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(k int) {
defer wg.Done()
m.Store(k, k*k)
}(i)
}
wg.Wait()
if _, ok := m.Load(50); !ok {
t.Error("Key 50 not found")
}
}
竞态检测可通过go test -race
启用,能捕捉到大部分并发问题。行业实践建议在CI流程中强制开启竞态检测。
基准测试
以Benchmark
开头的函数用于性能测试:
func BenchmarkFibonacci(b *testing.B) {
for i := 0; i < b.N; i++ {
Fibonacci(20)
}
}
运行时会自动调整b.N
直到获得稳定结果。关键注意事项:
– 避免在循环外初始化测试数据
– 使用b.ResetTimer()
排除初始化耗时
– 并行测试使用RunParallel
测试组织策略
分层测试体系
- 单元测试:验证独立函数/方法
- 集成测试:验证模块间交互
- 组件测试:验证完整子系统
测试固件管理
复杂依赖场景可使用TestMain
进行全局初始化:
func TestMain(m *testing.M) {
setup()
code := m.Run()
teardown()
os.Exit(code)
}
持续集成实践
现代CI/CD流程建议:
– 每次提交触发测试流水线
– 分层执行测试(单元测试->集成测试)
– 关键路径设置质量门禁
– 结合SonarQube等工具进行静态分析
典型GitHub Actions配置示例:
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: go test -v -cover -race ./...
常见陷阱与解决方案
- 时间依赖:使用可替换的时间接口
- 随机性:固定随机种子或模拟随机源
- 全局状态:通过依赖注入解耦
- IO操作:抽象为接口配合模拟实现
对于难以测试的遗留代码,可采用接缝测试技术,逐步重构至可测试状态。行业经验表明,测试代码与生产代码应保持相同质量标准,包括代码审查和重构。