全局函数

  1. func Short() bool // 报告是否使用-test.short标志
  2. func Verbose() bool // 报告是否使用-test.v标志

单元测试

  • 测试用例必须以 _test.go结尾
  • 测试用例的函数必须以 Test开头,Test后必须是大写。且形参必须为testing.T,例如:TestAdd(t testing.T)
  • 一个测试用例文件中可以有多个测试用例函数
  • 测试用例不需要main函数做为入口

my.go

  1. package utilTest
  2. func Add(n int) int{
  3. res :=0
  4. for i:=1;i<=n;i++{
  5. res+=i
  6. }
  7. return res-1
  8. }
  9. func Mul(n int) int{
  10. res :=1
  11. for i:=1;i<=n;i++{
  12. res*=i
  13. }
  14. return res
  15. }
  16. func Sub(i int,n int) int{
  17. return i-n
  18. }

my_test.go

  1. package utilTest
  2. import "testing"
  3. func TestAdd(t *testing.T) {
  4. res :=Add(10)
  5. if res!=55{
  6. t.Fatal("error")//输出错误信息,并终止程序(该测试,不会整个程序退出)
  7. }
  8. t.Log("success")//打印日志
  9. }
  10. func TestMul(t *testing.T) {
  11. res :=Mul(5)
  12. if res!=120{
  13. t.Fatal("error")
  14. }
  15. t.Log("success")
  16. }
  17. func TestSub(t *testing.T) {
  18. res :=Sub(100,50)
  19. if res!=50{
  20. t.Fatal("error")
  21. }
  22. t.Log("success")
  23. }

image.png

type T

T 是传递给测试函数的一种类型,它用于管理测试状态并支持格式化测试日志。测试日志会在执行测试的过程中不断累积, 并在测试完成时转储至标准输出。
当一个测试的测试函数返回时, 又或者当一个测试函数调用 FailNow 、 Fatal 、 Fatalf 、 SkipNow 、 Skip 或者 Skipf 中的任意一个时, 该测试即宣告结束。 跟 Parallel 方法一样, 以上提到的这些方法只能在运行测试函数的 goroutine 中调用。

  1. type T struct {
  2. // contains filtered or unexported fields
  3. }

func (c T) Error(args …interface{}) 调用 Error 相当于在调用 Log 之后调用 Fail
func (c
T) Errorf(format string, args …interface{}) 调用 Errorf 相当于在调用 Logf 之后调用 Fail
func (c T) Fail() 将当前测试标识为失败,但是仍继续执行该测试
func (c
T) FailNow() 将当前测试标识为失败并停止执行该测试
func (c T) Failed() bool Failed 用于报告测试函数是否已失败
func (c
T) Fatal(args …interface{}) 调用 Fatal 相当于在调用 Log 之后调用 FailNow
func (c T) Fatalf(format string, args …interface{}) 调用 Fatalf 相当于在调用 Logf 之后调用 FailNow
func (c
T) Log(args …interface{})

  • 对于测试来说,格式化文本只会在测试失败或者设置了 -test.v 标志的情况下被打印出来; 2)对于基准测试来说,为了避免 -test.v 标志的值对测试的性能产生影响, 格式化文本总会被打印出来

func (c T) Logf(format string, args …interface{})
func (c
T) Name() string 返回正在运行的测试或基准测试的名字
func (t T) Run(name string, f func(t T)) bool 执行名字为 name 的子测试 f
func (c T) Skip(args …interface{}) 调用 Skip 相当于在调用 Log 之后调用 SkipNow
func (c
T) SkipNow() 将当前测试标识为“被跳过”并停止执行该测试
func (c T) Skipf(format string, args …interface{})
func (c
T) Skipped() bool Skipped 用于报告测试函数是否已被跳过
func (t *T) Parallel() Parallel 用于表示当前测试只会与其他带有 Parallel 方法的测试并行进行测试

基准测试

  • 测试用例必须以 _test.go结尾
  • 测试用例的函数必须以 Test开头,Test后必须是大写。且形参必须为testing.B,例如:TestAdd(t testing.B)
  • 一个测试用例文件中可以有多个测试用例函数
  • 测试用例不需要main函数做为入口
  • 运行:
    • go test -bench . :执行所有的基准测试
    • go test -bench BenchmarkSub :执行单个基准测试
  • 注意:执行基准测试前,会将单元测试全部执行完,可以加 -run ^$ 来避免

my.go

  1. func Add(n int) int{
  2. res :=0
  3. for i:=1;i<=n;i++{
  4. res+=i
  5. }
  6. return res
  7. }

my_test.go

  1. package test
  2. import "testing"
  3. func BenchmarkTest(b *testing.B) {
  4. // 如果有初始化操作,b.ResetTimer()将让你跳过这些运行时间
  5. // b.ResetTimer()
  6. b.ReportAllocs() // 在report中包含内存分配信息
  7. for i:=1;i<b.N;i++{
  8. //如果每次循环迭代内部都有一些高成本的其他逻辑,使用b.StopTimer()和b.StartTimer()来暂停基准计时器。
  9. //b.StopTimer()
  10. // 需要操作的逻辑
  11. //b.StartTimer()
  12. Add(10)
  13. }
  14. }

image.png
BenchmarkTest-12 :-12 表示12个CPU线程执行
300000000 :总共执行了300000000次,越高越好
5.49 ns/op : 每次执行消耗5.49纳秒,越低越好
0 B/op : 每次执行分配了0字节内存,越低越好
0 allocs/op :表示每次执行分配了0次对象,越低越好

type B

B 是传递给基准测试函数的一种类型,它用于管理基准测试的计时行为,并指示应该迭代地运行测试多少次。
一个基准测试在它的基准测试函数返回时,又或者在它的基准测试函数调用 FailNow、Fatal、Fatalf、SkipNow、Skip 或者 Skipf 中的任意一个方法时,测试即宣告结束。至于其他报告方法,比如 Log 和 Error 的变种,则可以在其他 goroutine 中同时进行调用。
跟单元测试一样,基准测试会在执行的过程中积累日志,并在测试完毕时将日志转储到标准错误。但跟单元测试不一样的是,为了避免基准测试的结果受到日志打印操作的影响,基准测试总是会把日志打印出来。

  1. type B struct {
  2. N int
  3. // contains filtered or unexported fields
  4. }

func (c B) Error(args …interface{}) 调用 Error 相当于在调用 Log 之后调用 Fail
func (c
B) Errorf(format string, args …interface{})
func (c B) Fail() 将当前的测试函数标识为“失败”,但仍然继续执行该函数
func (c
B) FailNow() 将当前的测试函数标识为“失败”,并停止执行该函数
func (c B) Failed() bool 用于报告测试函数是否已失败
func (c
B) Fatal(args …interface{}) 调用 Fatal 相当于在调用 Log 之后调用 FailNow
func (c B) Fatalf(format string, args …interface{}) 调用 Fatalf 相当于在调用 Logf 之后调用 FailNow
func (c
B) Log(args …interface{})

  • Log 使用与 Println 相同的格式化语法对它的参数进行格式化, 然后将格式化后的文本记录到错误日志里面:
  • 对于测试来说, 格式化文本只会在测试失败或者设置了 -test.v 标志的情况下被打印出来; 对于基准测试来说, 为了避免 -test.v 标志的值对测试的性能产生影响, 格式化文本总会被打印出来

func (c B) Logf(format string, args …interface{})
func (c
B) Name() string 返回正在运行的测试或者基准测试的名字
func (b B) ReportAllocs() 打开当前基准测试的内存统计功能
func (b
B) ResetTimer() 对已经逝去的基准测试时间以及内存分配计数器进行清零
func (b B) Run(name string, f func(b B)) bool

  • 以并行的方式执行给定的基准测试。 RunParallel 会创建出多个 goroutine ,并将 b.N 分配给这些 goroutine 执行, 其中 goroutine 数量的默认值为 GOMAXPROCS

    1. testing.Benchmark(func(b *testing.B) {
    2. templ := template.Must(template.New("test").Parse("Hello, {{.}}!"))
    3. // RunParallel will create GOMAXPROCS goroutines
    4. // and distribute work among them.
    5. b.RunParallel(func(pb *testing.PB) {
    6. // Each goroutine has its own bytes.Buffer.
    7. var buf bytes.Buffer
    8. for pb.Next() {
    9. // The loop body is executed b.N times total across all goroutines.
    10. buf.Reset()
    11. templ.Execute(&buf, "World")
    12. }
    13. })
    14. })

    func (b *B) SetParallelism(p int)

  • 将 RunParallel 使用的 goroutine 数量设置为 p*GOMAXPROCS ,如果 p 小于 1 ,不产生任何效果

func (c B) Skip(args …interface{}) 调用 Skip 相当于在调用 Log 之后调用 SkipNow
func (c
B) Skipf(format string, args …interface{})
func (c B) SkipNow() 将当前测试标识为“被跳过”并停止执行该测试
func (c
B) Skipped() bool 报告测试是否已被跳过
func (b B) StartTimer() 在调用 StopTimer 之后恢复进行计时
func (b
B) StopTimer()

  • 停止对测试进行计时。 当你需要执行一些复杂的初始化操作, 并且你不想对这些操作进行测量时, 就可以使用这个方法来暂时地停止计时。

更多用法

注意:直接go test 不加参数,会执行当前目录下所有的测试用例
-run regexp 执行匹配到的单元测试
-bench regexp 执行匹配到的基准测试
-benchmem 打印内存消耗情况
-benchtime d 每个测试所用时间,默认1s,例如 -benchtime 3s
-count n 测试次数
-cpuprofile file 输出cpu使用情况
-memprofile file 输出内存使用情况
-v 详细输出:在所有测试运行时记录它们
-c 生成用于测试的可执行文件
-o 指定生成用于测试的可执行文件的文件名

一般写法:

  1. go test -v ./xxx/... -run .
  2. 指定文件夹或文件(通常为点 或则 ./xxx/...)
  3. 测试单个文件,一定要带上被测试的原文件

例如:go test -bench . -benchtime “3s” -cpuprofile profile_cpu func_test.go 后,会生成对应的文件
如何观察呢?

  • go tool pprof profile_cpu 进入交互模式
  • 常用命令

执行某个文件内所有的测试:go test -v aaa_test.go
执行某个文件内某个测试方法:go test -v -run TestAdd aaa_test.go

pprof UI

  1. go tool pprof -http=:8080 heap.out

TestMain

go test 功能,提高了开发和测试的效率。
有时会遇到这样的场景:
进行测试之前需要初始化操作(例如打开连接),测试结束后,需要做清理工作(例如关闭连接)等等。这个时候就可以使用TestMain()

  1. package hello
  2. import(
  3. "fmt"
  4. "testing"
  5. )
  6. func TestAdd(t *testing.T) {
  7. r := Add(1, 2)
  8. if r !=3 {
  9. t.Errorf("Add(1, 2) failed. Got %d, expected 3.", r)
  10. }
  11. }
  12. func TestMain(m *testing.M) {
  13. fmt.Println("begin")
  14. m.Run()
  15. fmt.Println("end")
  16. }
  17. // output
  18. begin
  19. === RUN TestAdd
  20. PASS: TestAdd (0.00s)
  21. PASS
  22. end
  23. ok hello 0.432s

注意

  1. //当TestMain被调用时,flag没有运行解析。
  2. //如果TestMain依赖于命令行标记,包括那些测试包,它应该调用flag.Parse()。显式解析。
  3. // A simple implementation of TestMain is:
  4. func TestMain(m *testing.M) {
  5. // call flag.Parse() here if TestMain uses flags
  6. os.Exit(m.Run())
  7. }

测试生成的图

image.png
框框越大,表示耗时越多
箭头越粗,表示耗时越多

image.png

记录:

-failfast flag does not apply across packages

https://github.com/golang/go/issues/33038
运行go test ./…-failfast 如果其中一个包出现故障,它将继续运行其他包的测试。
这是因为包不同,默认是以包为维度,做并行测试
另外还有一点,并发测试的标志,只适用于一个包内的测试用例,因为包不同默认就是并行的在测试

  • -p 指定的是不同包的并行数

工具推荐

https://github.com/pkg/profile,这个包能快速得到你想要的图,使用很方便

代码覆盖率

指令:go test -v -coverprofile=cover.out
或:go test -cover
测试单个文件:go test -cover params_check_test.go params_check.go

  1. === RUN TestSum
  2. --- PASS: TestSum(0.00s)
  3. PASS
  4. coverage: 100.0% of statements
  5. ok sum 0.009s
  6. $ ll cover.out
  7. -rw-rw-r-- 1 pirlo pirlo 1330 Jan 12 23:11 cover.out

生成html:
指令:go tool cover -html=cover.out -o coverage.html
执行完毕会生成一个html文件, 直接打开浏览器浏览便可

Subprocess tests

如果测试代码,可能通过os.Exit 的方式退出,则需要用到子测试
参考:https://talks.golang.org/2014/testing.slide#23

go test ./…

./… 会列出本项目下所有含.go文件的目录,会忽略 本项目下的子项目目录