Go的测试
测试主要包括:回归测试、集成测试、单元测试
一、单元测试
其中测试单元可以是函数,也可以是模块
规则:
1.所有测试文件都以_test.go结尾
2.测试函数命名规范:func TestXxx(*Testing.T)
3.初始化逻辑需要放置在TestMain中
以下是一个简单的测试:
测试函数HelloTom是否能打印出“tom”,如果不行,则启动错误打印
import "testing"
func HelloTom() string {
return "jerry"
}
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 文件名.go
很显然,测试不通过,实际输出为jerry
只要将HelloTom的输出改为"tom"就可以通过测试
import (
"github.com/stretchr/testify/assert"
"testing"
)
func HelloTom() string {
return "tom"
}
func TestHelloTom(t *testing.T) {
output := HelloTom()
expectOutput := "tom"
assert.Equal(t, expectOutput, output)
}
上述的代码中使用了来自github的库"github.com/stretchr/testify/assert" ,在判断输出是否相等的时候使用了assert.Equal()函数,合理使用第三方库可以减轻我们的开发负担
最终运行结果如下:
以上是单元测试的简单示例
1.2单元测试的评价标准
单元测试的标准主要是代码覆盖率,覆盖率越高则证明越多的代码经过了测试。下面我们通过一个例子来了解代码覆盖率。
测试代码覆盖率需要两个文件,分别为被测试的代码xxx.go和负责运行测试程序的xxx_test.go
首先是被测试代码Judge.go
// 判断学生成绩是否合格
func JudgePassLine(score int16) bool {
if score >= 60 {
return true
}
return false
}
接下来是测试程序Judge_test.go
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestJudgePassLineTrue(t *testing.T) {
isPass := JudgePassLine(70)
assert.Equal(t, true, isPass)
}
测试代码覆盖率的命令如下:
go test 测试程序 被测试的代码 --cover
示例: go test Judge_test.go Judge.go --cover
输出如下
输出显示代码覆盖率为66.7%,这是因为JudgePassLine一共有三行代码,其中score=70的时候,只会执行他的if语句和return true语句,因此覆盖了66.7%的代码。另外可以提高测试样例数量,使得覆盖率为100%
实际项目中,达到100%覆盖率是可望不可及的,一般项目对于主流层需要覆盖50%,而对于资金类的交易等关键操作,需要达到80%的覆盖率。对于测试,需要有以下要求:
- 测试分支完备独立,不重不漏,全面覆盖
- 测试单元粒度足够小,函数职责单一
1.3 对依赖的单元测试
我们在项目中需要依赖一些缓存、数据库或者文件等,对其的测试有两个要求:
- 幂等:重复运行一个测试其结果是一样的
- 稳定:单元测试是相互隔离的
1.3.1 对文件的处理
对文件的测试主要是用Mock机制
比如有一个函数是用于将文本中所有的11都替换为00,但是用传统方法是无法做到反复测试的!一旦运行了该函数,文本中的11就全部被替换为00了,如果再次运行,那么实际上这个函数的执行流程实际上是和第一次不一样的,而且一旦有人删除了该文件或者修改了该文件,也会改变测试结果,不满足幂等和稳定的要求
import (
"bufio"
"os"
"strings"
)
func ReadFirstLine() string {
open, err := os.Open("log")
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
}
此时我们就需要使用到Mock函数进行打桩。我们使用到的比较常用的Mock包是Monkey,其代码仓库地址是:https://github.com/bouk/monkey 。其中打桩可以使用函数A去替换需要被测试的函数B。其中Patch()为打桩函数,Unpatch()为卸桩函数。
施工中🚧待完善
二 基准测试
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(pb *testing.PB) {
for pb.Next() {
Select()
}
})
}