单元测试
基本知识
- 单元测试出现在开发阶段
- 单元测试的测试对象是各个函数、模块、接口等
- 单元测试规则
- 所有测试文件以
_test.go结尾 - 测试方法模式为:
func TestXxx(t *testing.T){} - TestMain方法
- 所有测试文件以
func TestMain(m *testing.M){
//测试前:数据装配、配置初始化等前置工作(如果是简单测试,则无需此方法)
code:=m.Run()
//测试后:释放资源等收尾工作
os.Exit(code)
}
- 测试命令
go test xx_test.go - 单元测试举例
高级部分
assert——断言
assert.Equal(t, expectOutput, output)常用于期望值与测试值的比较- 结果符合
- 结果不符合,则抛出错误,显示期待值和实际值
- 结果符合
- 在新的文件夹下 创建,终端使用
go mod init创建go.mod文件管理, - 导包 文件中import
"github.com/stretchr/testify/assert",终端使用go get github.com/stretchr/testify/assert导包 - 如下图
覆盖率
- 覆盖率指的是在测试时,被测试方法中已执行的代码/总代码
- 覆盖率越高越好
- 获得测试覆盖率的命令
go test xx_test.go xx.go --cover - 测试案例
tips
依赖+Mock
- 单元测试往往会依赖一些外部组件,如数据库、中间件(缓存)、文件等等
- 单元测试往往需要达到两个目标:幂等+稳定
- 幂等:(幂)多次运行程序,结果一致(等)
- 稳定:单元测试相互隔离,任何函数均可独立运行
为了使单元测试能够满足 幂等+稳定 的需求,又能够避免对外部组件的强依赖,引入Mock机制
- Mock机制常用包:
bou.ke/monkey - Mock用法:为一个(目标)函数打桩(替换)
- 对比强依赖:Mock机制主要是让方法不依赖外部文件,可以在任何地方进行单元测试
- 举例说明
package main
import (
"bufio"
"os"
"strings"
"testing"
"bou.ke/monkey"
"github.com/stretchr/testify/assert"
)
func ReadFirstLine() string {
open, err := os.Open("log.txt")
defer open.Close()
if err != nil {
return ""
}
scanner := bufio.NewScanner(open)
for scanner.Scan() {
return scanner.Text()
}
return ""
}
func ProcessFirstLine() string {
line := ReadFirstLine()
destline := strings.ReplaceAll(line, "11", "00")
return destline
}
func TestProcessFirstLine(t *testing.T) {
// 普通依赖方法(ReadFirstLine依赖于log文件)
// line := ProcessFirstLine()
// assert.Equal(t, "line00", line)
//使用mock机制
//打桩
monkey.Patch(ReadFirstLine, func() string {
return "line110"
})
//去除桩
defer monkey.Unpatch(ReadFirstLine)
line := ProcessFirstLine()
assert.Equal(t, "line000", line)
}
ReadFirstLine——打开log.txt文件,读取第一行返回(依赖外部文件)ProcessFirstLine——对获取到的字符串进行替换处理TestProcessFirstLine——测试ProcessFirstLine
- 在上述案例中,mock机制作用在对外部文件强依赖的
ReadFirstLine上,使其对ProcessFirstLine的输入由测试者控制,在不修改ProcessFirstLine和ReadFirstLine的前提下,解除了待测试方法ProcessFirstLine对外部文件的依赖
基准测试
- 基准测试的目的:测试性能,性能优化测试
- 基准测试命令:
go test -bench=. - 案例演示
package main
import (
"math/rand"
"testing"
)
var ServerIndex [10]int
func InitServerIndex() {
for i := 0; i < 10; i++ {
ServerIndex[i] = i + 100
}
}
func Select() int {
return ServerIndex[rand.Intn(10)]
}
func BenchmarkSelect(b *testing.B) {
InitServerIndex()
b.ResetTimer()
for i := 0; i < b.N; i++ {
Select()
}
}
func BenchmarkSelectParallel(b *testing.B) {
InitServerIndex()
b.ResetTimer()
b.RunParallel(func(p *testing.PB) {
for p.Next() {
Select()
}
})
}
结果如下:并行协程运算速度会比直接逐个运行慢
- 案例优化——提高select()的执行速率,可以加快并行协程运行速度
- 导入依赖
"github.com/bytedance/gopkg/lang/fastrand" - 使用
fastrand优化速度,使用FastSelect()替代Select()
- 导入依赖
func FastSelect() int {
return ServerIndex[fastrand.Intn(10)]
}
- 结果如下
总结
所有的测试均最好在独立的文件夹下,如果需要导入外部依赖则需要创建go.mod文件,而该文件会限制所在文件夹内不许出现同名函数,而且外部依赖需要使用go get 下载。
所有的测试案例自己打一遍后,才会真正掌握,因为有很多细节是需要自己去试着解决的,ppt上的内容和老师讲的并不能覆盖所有可能遇到的问题,何况自己实践过后印象总会更深一点。