> 我的 golang 开发笔记:https://github.com/stayfoo/stayfoo-hub
## 单元测试
一般有三类测试:功能测试(test)、性能测试(benchmark,也叫基准测试)、示例测试(example)。
### 编写规则
测试源码文件名称要求:`被测试源码文件的名称_test.go`, eg:要对 `demo.go` 进行测试,测试文件就是 `demo_test.go` 。
测试函数的名称和签名规定:
- `test` 测试函数,名称必须以 `Test` 为前缀; 只能有一个参数, 且参数类型:`*testing.T` 。
- `benchmark` 测试函数,名称必须以 `Benchmark` 为前缀; 只能有一个参数,且类型为:`*testing.B`。
- `example` 测试函数, 名称必须以 `Example` 为前缀;对参数列表没有强制规定。(与功能测试相比,更关注程序打印出来的内容)
### go test
`go test` 使测试代码执行,需要测试文件编写遵循上面的规则。
`go test` 执行的主要流程:
```
=== RUN TestFail
--- PASS: TestFail (0.00s)
main_test.go:49: Failed.
PASS
ok code/test 0.006s
# ok: 说明测试结果和预期一样
# code/test: 测试的代码包路径
# 0.006s : 此次对该代码包的测试所耗费的时间
```
- 参数 `-run`
```
#执行被测试包中的所有功能测试函数
go test
#同 go test
go test -run ''
#可以是一个函数的名字
go test -run=TestHello
#匹配部分函数名, 匹配以 TestHel 开头的测试函数, eg: TestHello
go test -run=Hel
#同上
go test -run Hel
```
不执行任何功能测试函数 `-run ^$`
```
go test -run ^$
# 和其他参数一起使用,比如只想执行 benchmark 函数:
go test -run ^$ -bench .
```
测试代码:
```go
func TestHello2(t *testing.T) {
t.Run("A=1", func(t *testing.T) {
fmt.Println("hello2 is A=1")
})
t.Run("A=2", func(t *testing.T) {
fmt.Println("hello2 is A=2")
})
t.Run("B=1", func(t *testing.T) {
fmt.Println("hello2 is B=1")
})
}
```
```
# 匹配运行 所有 TestHello 开头的测试函数,以及测试函数内部 A= 的子测试用例
go test -run Hello/A=
# 匹配运行 所有的测试函数,以及测试函数内部 A=1 的子测试用例
go test -run /A=1
```
- 缓存:
```
# 查看缓存目录的路径:
go env GO CACHE
# 手动删除所有缓存
go clean -cache
# 删除所有的测试结果缓存;不会删除任何构建结果缓存。
go clean -testcache
```
```
#设置环境变量 GODEBUG
# go 命令绕过任何的缓存数据,真正执行操作并重新生成所有结果,然后再去检查新的结果与现有的缓存数据是否一致
gocacheverify=1
```
### Benchmark
`go test` 加上 `-bench=.` 才会执行性能测试。
```
# 值 . 表示执行任意名称的性能测试函数
go test -bench=.
还可以是一个函数的名字 或者前面一部分:
go test -bench=BenchmarkGetStore
go test -bench=BenchmarkGetS
```
- 只执行 Benchmark
```
# 和其他参数一起使用,比如只想执行 benchmark 函数:
go test -run ^$ -bench .
```
- 运行结果解读
```go
func BenchmarkHandleData(b *testing.B) {
for i:=0; i<b.N; i++ {
HandleData()
}
}
```
```
go test -run ^$ -bench .
goos: darwin
goarch: amd64
BenchmarkHandleData-4 3000 401651 ns/op
PASS
ok command-line-arguments 1.259s
```
```
#1. 运行的性能测试函数:BenchmarkHandleData 被测试的函数是:HandleData
#2. 运行时使用的最大 P 数量为 4
#3. 3000 是指被测函数 HandleData 的执行次数
#4. 401651 ns/op = 性能测试函数执行时间 / 被测函数执行次数; 单次执行被测函数的平均耗时为 401651 纳秒;
BenchmarkHandleData-4 3000 401651 ns/op
```
> 最大 P 数量 ?
>
> P:processor ,是 G 和 M的中介,使 G 与 M 对接并得到真正的运行。Go运行时系统中,P越多,承载 G 的队列越多, G可运行的越多(最大P代表同时运行goroutine的能力)。 每一条承载 G 的队列,会不断的把可运行的 G 给空闲的 M 去对接。对接完成,被对接的 G 就真正运行在操作系统的内核级线程上了。每条队列之间会有联系,但都是独立运作的。
>
> G: goroutine ,协程
>
> M: machine ,代表着系统级线程,即操作系统内核级别的线程
>
使用 `go test -cpu` 来设置最大 P 数量. 或者 调用 `runtime.GOMAXPROCS` 函数改变最大 P 数量。模拟被测试程序在不同计算能力的计算机中的表现。
### 一些函数
#### t.Log()
打印常规的测试日志:(当测试成功的时候,不会打印)
```
t.Log()
t.Logf()
```
可以使用 `-v` 打印所有的常规测试日志:
```
go test -v
```
#### t.Fail()
```
func TestFail(t *testing.T) {
t.Fail()
//t.FailNow() //会让函数立即终止,该行代码之后的所有代码都不会执行。
t.Log("Failed.")
}
```
对于失败测试的结果, `go test` 命令并不会进行缓存。
测试失败,会把失败函数里面的常规日志打印出来。(`t.Log("Failed.")`)
#### t.Error()
`t.Error()` 相当于 `t.Fail()` + `t.Log()` 。
```
//相当于 t.Fail() + t.Log("Failed log.")
t.Error("Failed log.")
```
`t.Errorf()` 相当于 `t.Fail()` + `t.Logf()` 。
```
//相当于 t.Fail() + t.Log("Failed log.")
t.Errorf("Failed log.")
```
#### t.Fatal()
打印失败错误日志之后立即终止当前测试函数的执行,测试失败。
相当于最后调用了 `t.FailNow()` 。
```
t.Fatal("Failed log.")
t.Fatalf("Failed log.")
```
> 我的 golang 开发笔记:https://github.com/stayfoo/stayfoo-hub
有疑问加站长微信联系(非本文作者))