平时根据需求写代码.人工进行测试往往不会面面俱到,还会因为需求的改变繁琐的进行测试
通过完成一个测试函数,可以大大简化测试的步骤,并且在需求该变的时候只需要改变一下测试的输入与期望
进行基准测试时往往是对函数的算法进行测验,有时后一个算法在测试数据的基量不同时测试出的效果会不同
我们需要对不同数量级的样本进行测试的时候。可以自己写一个盒子作为跳板,测试什么数量级的数据就只
进行当前数量级的测试
测试的时候命令为:go test -bench=.(将所有的跳板函数均执行一遍)
或者=特定函数进行特定函数测试
-benchtime=time
(在被测试函数在基准函数默认的时间内执行不完时可以拿这个参数进行时间的扩充)
在进行基准函数测试的时候,可能需要进行一些前置工作,如果感觉前置工作浪费时间可以使用
b.ResetTimer()对计时器进行重置
测试函数代码如下:
//文件名 subStr_test.go
package main
import (
"reflect"
"testing"
)
func BenchmarkSubStr(b *testing.B) {
for i := 0; i < b.N; i++ {
res := subStr("qwe:qw:es:wqe", ":")
if !reflect.DeepEqual(res, []string{"qwe", "qw", "es", "wqe"}) {
b.Errorf("不匹配")
}
}
}
func benchmarkFib(b *testing.B, n int) {
for i := 0; i < b.N; i++ {
Fib(n)
}
}
func BenchmarkFib5(b *testing.B) { benchmarkFib(b, 5) }
func BenchmarkFib10(b *testing.B) { benchmarkFib(b, 10) }
func BenchmarkFib15(b *testing.B) { benchmarkFib(b, 15) }
func BenchmarkFib20(b *testing.B) { benchmarkFib(b, 20) }
被测函数代码如下:
//文件名 subStr.go
package main
import (
"fmt"
"strings"
)
func subStr(str, stre string) []string {
index := strings.Index(str, stre)
var theSub []string
for index >= 0 {
// 先将分隔符前面的数据进行保存
temp := str[:index]
// 将字符串往后移
str = str[index+1:]
// 重新获取下标
index = strings.Index(str, stre)
if temp != "" {
theSub = append(theSub, temp)
} else {
continue
}
}
theSub = append(theSub, str)
return theSub[:]
}
// 菲薄那契数列
func Fib(n int) int {
if n < 2 {
return n
}
return Fib(n-1) + Fib(n-2)
}
func main() {
fmt.Println(subStr("q:w:ec:wer:cd:scn:cj:c:is:icc:cin:si", ":"))
fmt.Printf("%#v\n", subStr("q:w:ec:wer:cd:scn:cj:c:is:icc:cin:si", ":"))
fmt.Println(123)
}
将以上两个文件放在同一目录下
执行测试命令 go test
得到测试结果
goos: windows
goarch: amd64
cpu: Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz
BenchmarkSubStr-8 1213681 1012 ns/op 352 B/op 14 allocs/op
PASS
ok _/d_/Go语言学习笔记/go语言语法部分/go语言进阶语法/8.单元测试/基准测试 2.410s
对于一个函数的测试,不仅仅是一组测试用例,所以我们需要写测试组,
对一个函数进行测试,测试的时候一般用集合键值对构成,每一个键对应一组测试数据
进行组测试的时候可能会有个别的组出错,所以使用子测试可以对某个案例进行单独测试
这就是测试组与子测试的产生背景
测试函数如下:
package main
import (
"reflect"
"testing"
)
type testS struct {
str string
ste string
want []string
}
//进行组测试,批量进行测试,如果有哪个地方不满足则进行异常抛出
// func TestSubStr(t *testing.T) {
// testMap := map[string]testS{
// "case_1": {"123:eqwe:123", ":", []string{"123", "eqwe", "123"}},
// "case_2": {"13:eqwe3:1@23", "3:", []string{"1", "eqwe", "1@23"}},
// "case_3": {"12@3@:@eq@we@:1@23", "@:", []string{"12@3", "@eq@we", "1@23"}},
// "case_4": {"123:eq@we:1@2@3", "q@", []string{"123:e", "we:1@2@3"}},
// "case_5": {"123:eqwe:123", "2", []string{"1", "3:eqwe:1", "3"}},
// "case_6": {"123:eqwe:123", "eqwe", []string{"123:", ":123"}},
// }
// for k, v := range testMap {
// res := subStr(v.str, v.ste)
// if !reflect.DeepEqual(res, v.want) {
// t.Errorf("%v want:%#v got:%#v", k, v.want, res)
// }
// }
// }
// 进行子测试,可以针对某个子样本进行测试
func TestSubStr(t *testing.T) {
testMap := map[string]testS{
"case_1": {"13:eqwe:123", ":", []string{"123", "eqwe", "123"}},
"case_2": {"3:eqwe3:1@23", "3:", []string{"1", "eqwe", "1@23"}},
"case_3": {"2@3@:@eq@we@:1@23", "@:", []string{"12@3", "@eq@we", "1@23"}},
"case_4": {"123:eq@we:1@2@3", "q@", []string{"123:e", "we:1@2@3"}},
"case_5": {"23:eqwe:123", "2", []string{"1", "3:eqwe:1", "3"}},
"case_6": {"123:eqwe:123", "eqwe", []string{"123:", ":123"}},
}
for k, v := range testMap {
t.Run(k, func(t *testing.T) {
res := subStr(v.str, v.ste)
if !reflect.DeepEqual(res, v.want) {
t.Errorf("want:%#v got:%#v", v.want, res)
}
})
}
}
待测函数如下:
package main
import (
"fmt"
"strings"
)
func subStr(str, stre string) []string {
index := strings.Index(str, stre)
var theSub []string
for index >= 0 {
// 先将分隔符前面的数据进行保存
temp := str[:index]
// 将字符串往后移
str = str[index+len(stre):]
// 重新获取下标
index = strings.Index(str, stre)
if temp != "" {
theSub = append(theSub, temp)
} else {
continue
}
}
theSub = append(theSub, str)
return theSub[:]
}
func main() {
fmt.Println(subStr("q:w:ec:wer:cd:scn:cj:c:is:icc:cin:si", ":c"))
fmt.Printf("%#v\n", subStr("q:w:ec:wer:cd:scn:cj:c:is:icc:cin:si", ":"))
fmt.Println(123)
}
组测试命令仍使用 go test
--- FAIL: TestSubStr (0.00s)
--- FAIL: TestSubStr/case_1 (0.00s)
subStr_test.go:46: want:[]string{"123", "eqwe", "123"} got:[]string{"13", "eqwe", "123"}
--- FAIL: TestSubStr/case_2 (0.00s)
subStr_test.go:46: want:[]string{"1", "eqwe", "1@23"} got:[]string{"eqwe", "1@23"}
--- FAIL: TestSubStr/case_3 (0.00s)
subStr_test.go:46: want:[]string{"12@3", "@eq@we", "1@23"} got:[]string{"2@3", "@eq@we", "1@23"}
--- FAIL: TestSubStr/case_5 (0.00s)
subStr_test.go:46: want:[]string{"1", "3:eqwe:1", "3"} got:[]string{"3:eqwe:1", "3"}
FAIL
exit status 1
FAIL _/D_/Go语言学习笔记/go语言语法部分/go语言进阶语法/8.单元测试/组测试与子测试 0.155s
例如单独测试case_1,使用的命令为
go test -v -run=TestSubStr/case_1(等号后面跟的是上面组测试失败案例FAIL后面的目录)
=== RUN TestSubStr
=== RUN TestSubStr/case_1
subStr_test.go:46: want:[]string{"123", "eqwe", "123"} got:[]string{"13", "eqwe", "123"}
--- FAIL: TestSubStr (0.00s)
--- FAIL: TestSubStr/case_1 (0.00s)
FAIL
exit status 1
FAIL _/D_/Go语言学习笔记/go语言语法部分/go语言进阶语法/8.单元测试/组测试与子测试 0.186s
package main1
import (
"fmt"
"os"
)
func main() {
// os.Args可以在执行函数的时候传递参数,但是对于-name=xxx没有办法将其解析
if os.Args != nil {
for index, temp := range os.Args {
fmt.Println("第", index+1, "个参数是:", temp)
}
}
fmt.Println("hello")
}
相对于os.Args来说,flag.Args使用起来更加方便
package main
import (
"flag"
"fmt"
"time"
)
func main() {
// 第一个参数为对应的参数,第二个参数为默认值,第三个参数为提示
// 返回的是对应类型的指针
// name := flag.String("name", "Tom", "输入name")
// sex := flag.Bool("sex", true, "是不是男性")
// age := flag.Int("age", 10, "年龄")
// flag.Parse()
// fmt.Println(*name, *sex, *age)
var name string
var age int
var sex bool
flag.StringVar(&name, "name", "Tom", "输入name")
flag.BoolVar(&sex, "sex", true, "是不是男性")
flag.IntVar(&age, "age", 10, "年龄")
tim := flag.Duration("time", time.Hour, "时间")
// 将输入的数据进行解析,不使用这句话的话不能获取到name sex age等属性对应的值
flag.Parse()
fmt.Println(name, sex, age, *tim)
fmt.Println(flag.Args()) //以切片的方式返回命令行之外的其他参数
fmt.Println(flag.NArg()) //返回命令行之外的其他参数的个数
fmt.Println(flag.NFlag()) //返回使用命令行的参数的个数
}
pprof调试工具进行调试,主要看待测模块在内存于时间上的效益
调试的时候只会显示耗费时间空间较多的代码段.
生成调试代码块的文件:go run xx.exe -cpu....
使用go语言工具查看代码块所存在的问题:go tool pprof cpu.pprof
package main
import (
"flag"
"fmt"
"os"
"runtime/pprof"
"time"
)
// 一段有问题的代码
func logicCode() {
var c chan int
for {
select {
case v := <-c:
fmt.Printf("recv from chan, value:%v\n", v)
default:
}
}
}
func main() {
var isCPUPprof bool
var isMemPprof bool
//获取接收到的参数
flag.BoolVar(&isCPUPprof, "cpu", false, "turn cpu pprof on")
flag.BoolVar(&isMemPprof, "mem", false, "turn mem pprof on")
flag.Parse()
//判断进行什么测试,然后执行分支,并将信息保存到相应的文件内。
if isCPUPprof {
file, err := os.Create("./cpu.pprof")
if err != nil {
fmt.Printf("create cpu pprof failed, err:%v\n", err)
return
}
pprof.StartCPUProfile(file)
defer pprof.StopCPUProfile()
}
for i := 0; i < 8; i++ {
go logicCode()
}
time.Sleep(20 * time.Second)
if isMemPprof {
file, err := os.Create("./mem.pprof")
if err != nil {
fmt.Printf("create mem pprof failed, err:%v\n", err)
return
}
pprof.WriteHeapProfile(file)
file.Close()
}
}
这里对常用的测试函数,子测试进行了详细的讲解,对于pprof参数调优仅仅做了个介绍,可以根据自己的兴趣在网上找一部分pprof的例子深入学习,进行自动化测试非常方便。