这是我参与「第五届青训营」笔记创作活动的第3天。
测试分类
单元测试:开发阶段,开发者对单独的函数、模块做功能验证。
集成测试:对系统功能维度做测试验证。
回归测试:回归一些固定的主流程场景。
单元测试,覆盖率最大,成本最低,所以一定程度上决定着代码的质量。
单元测试
规则
测试文件以_test.go结尾。
测试函数 func TestXx(t *testing.T)。
初始化逻辑放到TestMain中。
func TestMain(m *testing.M){
//数据装载 配置初始化
code:=m.Run() //运行package下所有单测
//释放资源
os.Exit(code)
}
例子
func TestHelloTom(t *testing.T) {
output := HelloTom()
expectOutput := "Tom"
if output != expectOutput {
t.Errorf("Expected %s do not match actual %s", expectOutput, output)
}
}
go test执行测试。
assert
可以使用一些assert包进行比较。
import "github.com/stretchr/testify/assert"
assert.Equal(t, expectOutput, output)
覆盖率
go test a_test.go a.go -cover计算覆盖率。
计算测试执行过的代码行数占代码总行数的比例。
Tips
一般覆盖率50%~60%,资金型服务较高覆盖率80%+。
测试分支相互独立、全面覆盖。
测试单元粒度足够小,函数单一职责。
Mock测试
复杂的工程项目会有很多外部依赖,而单元测试需要保证幂等性和稳定性。
幂等性,是指测试每次运行都能产生相同的结果。
稳定性,是指测试能相互隔离,能在任何时间任何环境运行。
func TestProcessFirstLineWithMock(t *testing.T) {
monkey.Patch(ReadFirstLine, func() string {
return "line110"
})
defer monkey.Unpatch(ReadFirstLine)
line := ProcessFirstLine()
assert.Equal(t, "line000", line)
}
monkey是一个开源的mock测试库。
替换读取文件的函数,使测试不依赖本地文件。
用打桩函数替换原函数,原理是在运行时替换内存中函数的地址。
基准测试
基准测试,是指测试程序的运行性能、CPU消耗等。
开发中经常会遇到性能瓶颈,为了定位问题需要做性能分析,因此要用到基准测试。
例子
func BenchmarkSelect(b *testing.B) {
InitServerIndex()
b.ResetTimer()
for i := 0; i < b.N; i++ {
Select()
}
}
N值反复递增循环测试。
准备操作不应该作为基准测试的范围,使用ResetTimer函数重置计时器。
func BenchmarkSelectParallel(b *testing.B) {
InitServerIndex()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
Select()
}
})
}
多协程并发测试。
go test -bench="."执行基准测试。
优化
发现代码在并发情况下存在劣化,主要原因是rand为了保证全局的随机性和并发安全,持有了一把全局锁。
使用公司开源的fastrand,牺牲了一定的数列一致性,能够大幅度提升性能。