1.测试内容
主要包括三点:单元测试,集成测试,回归测试
测试成本逐渐降低,代码覆盖率逐渐上升
1.1 单元测试
将参数输入测试单元(函数,接口,模块等),将输出与期望进行比对的过程
可以保证代码质量
3.1.1 单元测试规则
- 所有测试文件均以_test.go结尾
- func TestXxx(t *testing.T),要注意Test后面的首字母不能小写,否则无法运行
- 初始化逻辑放在TestMain中
3.1.2 单元测试示例
我们开发了一个HelloTom,本来是要输出Tom但是输出了Jerry,对该函数进行单元测试
func HelloTom() string {
return "Jerry"
}
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestHelloTom(t *testing.T) {
output := HelloTom()
expectoutput := "Tom"
assert.Equal(t, expectoutput, output, "they should be equal")
}
使用go test [flag][package]对模块进行测试
这里我们可以使用github.com/stretchr/testify/assert包中的Equal方法来对期望输出与输出进行比较
引入依赖的方法:
点击Goland的Setting,选择GO模块的依赖项集成化,设置GOPROXY变量值:goproxy.io,direct,然后选择下载依赖项启用即可
在运行项目前使用go mod tidy命令将依赖项下载好
结果:
3.1.5 覆盖率
如何评估项目的测试水准?
代码覆盖率越完备,整体质量越好
func TestJudgePassLineTrue(t *testing.T) {
output := JudgePassLine(70)
expectoutput := true
assert.Equal(t, expectoutput, output, "they should be equal")
}
func TestJudgePassLineFalse(t *testing.T) {
output := JudgePassLine(50)
expectoutput := false
assert.Equal(t, expectoutput, output, "they should be equal")
}
进入项目根目录,在终端使用命令:go test Xxxx_test.go Xxxx.go —cover(cover就是覆盖的意思)
将两种不同分支的输出分别进行测试会发现,代码测试的覆盖率已经到了100
对不同分支进行测试可以提高测试的完备率
一般覆盖率50——60即可,80就是较高了
测试分支相互独立,全面覆盖
测试的单元粒度足够小,函数单一职责
3.2 单元测试——依赖
单元测试的两个目标:幂等,稳定
幂等指的是:每次运行结果一样
稳定是指:单元测试相互隔离,任何时间都可以对单元进行测试
会用到mock机制
有些测试依赖一些特等的文件或者组件:如读文件的测试,数据库的测试等
这样的话就无法保证稳定性
3.4 Mock测试
使用mockey包进行mock测试
可以快速为一个函数打桩:用一个函数替换另一个函数
func Patch(target(原函数,目标被替换的函数),replacement(打桩函数) interface{}) *PatchGuard
还需要定义UnPatch,为了在测试完把目标桩卸载掉
func Unpatch(target interface{}) bool
下面的例子我们使用mockey包对一个读文件函数做mock测试使其不依赖本地文件
package mock
import (
"bufio"
"os"
"strings"
)
func ReadFirstLine() string {
open, err := os.Open("test.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
}
使用monkey包进行打桩操作,将原先文本读出改为固定返回值
package mock
import (
"github.com/bouk/monkey"
"github.com/stretchr/testify/assert"
"testing"
)
func TestProcessFirstLine(t *testing.T) {
monkey.Patch(ReadFirstLine, func() string {
return "Line110"
}) //使用monkey替换ReadFirstLine函数为一个固定的返回值
defer monkey.Unpatch(ReadFirstLine)
Line := ProcessFirstLine()
assert.Equal(t, "Line000", Line, "they should be equal")
}
3.5基准测试
Golang内置了基准测试框架,对代码进行性能分析
基准测试与单元测试类似,也需要以_test为文件结尾
示例:负载均衡
package benchTest
import "math/rand"
var ServerId[10] int
func InitServerIndex(){
for i:=0;i<10;i++{
ServerId[i]=i+100
}
}
func Select() int{
return ServerId[rand.Intn(10)]
}
func FastSelect() int {
return ServerId[fastrand.Uint32n(10)]
}
选择服务器id的函数
基准测试函数以BenchmarkXxx为name,与Testxxx类似,主要是对于性能的测试
package benchTest
import "testing"
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) { //并行测试,实质是多个goroutine同时执行Select()
for pb.Next() {
Select()
}
})
}
func BenchmarkFastSelect(b *testing.B) {
InitServerIndex()
b.ResetTimer()
for i := 0; i < b.N; i++ {
FastSelect()
}
}
func BenchmarkFastSelectParallel(b *testing.B) {
InitServerIndex()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
FastSelect()
}
})
}
运行每个Benchmark,我们会发现并行时的性能比一般的要差一些,是因为有全局锁,性能劣化了
使用fastrand明显性能更好