简单介绍:单元测试、Go test
这里记录的是我个人不太熟练的语法和知识
详细内容移步:李文周的博客(非常详细)
https://www.liwenzhou.com/posts/Go/16_test/#autoid-0-0-0
(因为当时没什么函数测试,所以就抄了,嘿嘿嘿)
#测试函数
在一个splist.go文件内写入需要被测试的函数,文件名可以随意,但是为了‘值观对应’一般会与文件名相同;
在一个.go文件中,如果存在多个需要被测试的函数,则需要…一般是什么文件就_test什么文件…
在Goland IDE中可以通过按住Ctrl键并指向被测试函数并按下会出现相关内容
//切分字符串
// Split 将s按照spe进行切割,返回一个字符串的切片
// Split("我爱你","爱") => ["我","你"]
func Split(s, sep string) (ret []string) {
ret = make([]string, 0, strings.Count(s, sep)+1)
idx := strings.Index(s, sep)
for idx > -1 {
ret = append(ret, s[:idx]) // append()函数在容量不够的情况下会申请内存
s = s[idx+len(sep):]
idx = strings.Index(s, sep)
}
ret = append(ret, s)
return
}
这样被测试函数就定义好了
#测试用例
在文件夹中创建split_test.go的测试文件,也可以在别的文件夹中创建,但是要导包。
测试函数的首字母必须是大写Test+被测试函数名
**还需要接收一个*testing.T类型参数
func TestSplit(t *testing.T) {
//got := Split("我爱你", "爱")
//want := []string{"我", "你"}
//切片不能直接比较,channel、map同样
got := Split("a:b:c", ":")
want := []string{"a", "b", "c"}
if !reflect.DeepEqual(got, want) { //DeepEqual用与比较"不能比较的"和"引用类型的比较"
t.Errorf("want:%v got:%v", want, got)
}
}
go test 一下
当然,只测试一个示例就太草率了
通过map类型,尽可能覆盖输入输出的多种情况。
func TestSplit(t *testing.T) {
type test struct {
input string
sep string
want []string
}
tests := map[string]test{
"simple": {input: "a:b:c", sep: ":", want: []string{"a", "b", "c"}},
"chinese": {input: "傻-逼", sep: "-", want: []string{"傻", "逼"}},
}
for name, tc := range tests {
got := Split(tc.input, tc.sep)
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("name %v failed, want:%v got:%v ", name, tc.want, got)
}
}
}
当然测试时候可能需要查看具体某一个测试用例的具体情况
go test -v 需要这个t.Run()函数才能能看到具体是某个Test函数的具体情况
func TestSplit(t *testing.T) {
type test struct {
input string
sep string
want []string
}
tests := map[string]test{
"simple": {input: "a:b:c", sep: ":", want: []string{"a", "b", "c"}},
"chinese": {input: "傻-逼", sep: "-", want: []string{"傻", "逼"}},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
got := Split(tc.input, tc.sep)
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("name %v failed, want:%v got:%v ", name, tc.want, got)
}
})
}
}
此外还可以单独测试指定用例
go test -run=Split/chinese 单独跑一个测试用例(不演示了)
这里的chinese是指map类型里面的"chinese"
效果与单独测试一个函数相同
#重置时间
func BenchmarkSplit(b *testing.B) {
time.Sleep(5 * time.Second) // 假设需要做一些耗时的无关操作
b.ResetTimer() // 重置计时器
for i := 0; i < b.N; i++ {
Split("沙河有沙又有河", "沙")
}
}
#查看覆盖率
在我自己看来,这是最(迪奥)的东西。
go test -cover
还可以具体查看覆盖了那些代码
go test -coverprofile=c.out
覆盖率可视化输出到c.out(随意文件名.随意文件后缀名)
go tool cover -html=c.out
将c.out文件以html方式打开,直接将文件拖到浏览器是不行的,必须用上述命令。
#性能基准测试
原理:调用非常非常多次看看函数的:平均执行时间、内存申请消耗、等等(???)。
func BenchmarkSplit(b *testing.B) {
// b.N 不是固定的数 但至少保证跑够1秒
for i := 0; i < b.N; i++ {
Split("前中后", "中")
}
}
go test -bench=Split
数据解释
BenchmarkSplit-8 (-8 go maxprocess真正干活的进程数) (CPU干活数)
12269937 执行次数
97.5 ns/op 每一次操作耗费了97.5纳秒
但是,一般我们测试的时候会加上**-benchmem**,统计内存分配情况。
数据解释
32B/op 每次操作消耗的内存
1 allocs/op 每次操作的内存申请数
#性能比较函数
测试函数的性能一般是使用对比方法,但上面的测试方式多少有点单薄,所以大多数情况是使用比较的方式。
在同一个问题下,有多种的处理方法。
比如我们想知道一个函数处理1W次和10W次的耗时是多少,或则对两个不同算法实现相同的效果时,新能差别是多少。
(这是抄的)
被测试函数
func Asd(n int) int {
i := 0
for {
if n == 1 {
return i
}
n = n / 2
i++
}
}
测试函数
func benchmarkFib(b *testing.B, n int) {
for i := 0; i < b.N; i++ {
Fib(n)
}
}
func BenchmarkFib1(b *testing.B) { benchmarkFib(b, 1) }
func BenchmarkFib2(b *testing.B) { benchmarkFib(b, 2) }
func BenchmarkFib3(b *testing.B) { benchmarkFib(b, 3) }
func BenchmarkFib10(b *testing.B) { benchmarkFib(b, 10) }
func BenchmarkFib20(b *testing.B) { benchmarkFib(b, 20) }
func BenchmarkFib40(b *testing.B) { benchmarkFib(b, 40) }
**go test -bench=.**test全部
go test -bench=Fib40 -benchtime=20stest Fib40 跑够20秒
#并行测试
func BenchmarkSplitParallel(b *testing.B) {
// b.SetParallelism(1) // 设置使用的CPU数
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
Split("沙河有沙又有河", "沙")
}
})
}
https://www.liwenzhou.com/posts/Go/16_test/#autoid-0-0-0
https://www.bilibili.com/video/av66788366/