Go Test

JunChow520 · · 2477 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Golang拥有一套单元测试和性能测试系统 - go test 命令

  • 单元测试(UT, unit testing)

单元测试是指对软件中的最小可测试单元进行检查和验证,对于单元测试中单元的含义,一般要根据实际情况去判定其具体含义。

单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。

  • 性能测试

性能测试又称为基准测试,是一种测试代码执行性能的方式。

基准测试主要是通过测试CPU和内存的分配来展示被测试代码的性能,进而找到性能更优的解决方案。

测试命令

  • 测试文件名必须为*_test.go

go test命令会自动读取源码目录下名为*_test.go的文件,生成并运行测试用的可执行文件。

$ go help test
$ go help testflag

执行模式

go test具有两种执行模式

  • 本地目录模式local mode

本地模式即直接cd到特定目录中运行go test,不用指定目录。

在没有包参数(例如go testgo test -v)调用时发生,在此模式下go test编译当前目录中找到的包和测试,然后运行测试二进制文件。在本地目录模式下缓存是禁用的。包测试完成后go test会打印一行概要,显示测试状态、包名、运行时间。

  • 包列表模式package list mode

在任意目录中运行go test,需指定要测试的package目录。

在使用显示包参数调用go test时发生,例如go test mathgo test ./...甚至是go test .。在此模式下go测试编译并测试在命令行列出每个包。如果一个包测试通过,go test只打印最终的ok总结行。如果一个包测试失败,go test将输出完整的测试输出。如果使用-bench-v标志则go test会输出完整的输出,甚至是通过包测试,以显示锁请求的基准测试结果或详细日志记录。

local mode中无缓存功能,在package list mode中测试成功的会被缓存,若后续没有改过测试函数,也没有改过被测试函数,那么将直接使用缓存的结果以加速整个测试。

// 清理所有缓存,包括测试缓存。
$ go clean -cache

//只清理测试缓存
$ go clean --testcache

测试用例

*_test.go文件中有三种类型的函数:测试函数、基准测试、示例函数

  • 功能测试:函数名必须以Test开头,函数参数必须是*testing.T。测试成立逻辑行为是否正确。
  • 性能测试:函数名必须以Benchmark开头,函数参数必须是*testing.B。测试函数的性能。
  • 示例测试:函数名必须以Example开头,函数参数无要求。为文档提供示例文档。

测试函数中某条测试用例执行结果与预期不符可调用t.Error()t.Errorf()方法记录日志并标记测试失败

测试用例具有四种形式

  • 基本测试用例:TestXxx(t *testing.T)
  • 压力测试用例:BenchmarkXxx(b *testing.B)
  • 测试控制台输出:Example_Xxx()
  • 测试主函数:TestMain(m *testing.M)

功能测试

  • 功能测试用于测试程序的一些逻辑行为是否正确
  • go test命令会调用测试函数并报告测试结果是PASSFAIL
  • 每个功能测试函数必须导入testing
  • 功能测试函数是以Test*为函数前缀的函数,但首字母不能是小写字母。
$ vim ./test/cache_test.go
package test

import (
    "fmt"
    "testing"
)

func TestMain(m *testing.M) {
    fmt.Printf("test main\n")
    m.Run()
}

func TestCache(t *testing.T) {
    fmt.Printf("Test Cache\n")
}

测试文件以TestMain函数作为初始化test的入口,可以在m.Run()函数前完成初始化操作。若没有TestMain中调用m.Run()则除了TestMain()以外的其他test都不会执行。如果没有重写TestMain()则默认都会执行test。

测试整个文件

$ go test
test main
Test Cache
PASS
ok      gfw/test        0.291s

显示测试详情

  • 直接输入go test不会打印t.Log记录的信息,打印日志可采用go test -v
  • go test -v显示测试的详细命令
$ go test -v 
test main
testing: warning: no tests to run
PASS
ok      gfw/test        0.293s

测试单个文件需要带上被测试的源文件

$ go test -v cache_test.go
test main
=== RUN   TestCache
Test Cache
--- PASS: TestCache (0.00s)
PASS
ok      command-line-arguments  0.294s
  • PASS表示测试成功
  • ok表示测试通过,command-line-arguments表示测试用例需要用到的一个包名,0.294s表示测试耗费的时长。

测试单个函数

  • go test指定文件时默认会执行文件内的所有测试用例
  • go test -run可使用-run参数选择需要的测试用例单独执行
$ go test -v cache_test.go -test.run TestCache
test main
=== RUN   TestCache
Test Cache
--- PASS: TestCache (0.00s)
PASS
ok      command-line-arguments  0.291s

单元测试日志

  • 每个测试用例可能并发执行,使用testing.T提供的日志输出可以保证日志跟随测试上下文一起打印输出。
  • testing.T提供的日志输出方法
日志方法 描述
Log 打印日志,同时结束测试。
Logf 格式化打印日志,同时结束测试。
Error 打印错误日志,同时结束测试。
Errorf 格式化打印错误日志,同时结束测试。
Fatal 打印致命日志,同时结束测试。
Fatalf 格式化打印致命日志,同时结束测试。

标记单元测试结果

  • 当需要终止当前测试用例时可使用t.FailNow()方法
  • 当只需要标记错误而不终止测试可采用t.FailNow()方法
package test

import (
    "testing"
)

func TestCache(t *testing.T) {
    t.Log("TEST CACHE START...")
    t.FailNow()
    t.Log("TEST CACHE END...")
}
$ go test -v -run TestCache cache_test.go
=== RUN   TestCache
    cache_test.go:8: TEST CACHE START...
--- FAIL: TestCache (0.00s)
FAIL
FAIL    command-line-arguments  0.306s
FAIL

基准测试 - benchmark test

基准测试框架对一个测试用例的默认测试时间为1秒,开始测试时当以Benchmark开头的基准测试用例函数返回时还不到1秒,那么testing.B中的N值将按1、2、5、10、20、50...递增,同时以递增后的值重新调用基准测试用例函数。

  • 基准测试可测试一段程序的运行性能及耗费CPU的程度
  • go test工具提供了压力测试功能的支持 -- benchmark test
  • go test的基准测试提供了将目标代码执行N次统计运行时间从而实现压测的功能

与单元测试类似只要在项目的*_test.go文件中写入BenchmarkXxx(b *testing.B)函数即可实现基准测试函数的编写。执行基准测试命令go test -bench=.即可获取测试结果。

  • 测试文件必须以*_test.go结尾
  • 测试文件必须导入testing
  • 测试函数名必须以Benchmark开头,函数签名必须接收一个指向testing.B类型的指针,无返回值。
  • 测试函数循环上限为b.N
package test

import (
    "fmt"
    "testing"
)

func BenchmarkCache(b *testing.B) {
    for i := 0; i < b.N; i++ {
        fmt.Printf("%v\n", i)
    }
}

例如:

$ vim ./test/cache_test.go
package test

import (
    "fmt"
    "testing"
)

func BenchmarkCache(b *testing.B) {
    fmt.Printf("%v\n", "BENCHMARK...")
}
$ go test -bench=BenchmarkCache -benchmem -benchtime=10s
BENCHMARK...
goos: windows
goarch: amd64
pkg: gfw/test
BenchmarkCache-6        BENCHMARK...
BENCHMARK...
BENCHMARK...
BENCHMARK...
BENCHMARK...
1000000000             0 B/op          0 allocs/op
PASS
ok      gfw/test        0.298s

意味着基准测试函数在测试时间内循环了1000000000次

go test -bench=".*" [-v]目录下全部单元测试和基准测试

可通过-bench=BenchmarkXxx目录下指定基准测试函数

$ go test -bench=BenchmarkCache

可通过-benchtime来指定测试的执行时间,默认不小于1秒。

$ go test -bench=. -benchtime=20s

可通过-benchmem显示每次操作分配内存的次数(allocs/op)和字节数(B/op)。

$ go test -bench=. -benchmem

控制计数器

当测试需要一定的启动和初始化时间,若从Benchmark()函数开始计时时会很大成都上影响测试结果的精准性。

testing.B提供了一系列的方法可以方便地控制计时器,从而让计时器只在需要的区间进行测试。

package test

import (
    "testing"
)

func BenchmarkCache(b *testing.B) {
    //b.ResetTimer()//重置计时器
    //b.StopTimer()  //停止计时器
    //b.StartTimer() //开始计时器
}

并行执行测试 - Parallelizing tests

默认情况下,指定包的测试是按照顺序执行的,但也可以通过测试的函数内部使用t.Parallel()来标志某些测试也可以被安全的并发执行。

package test

import (
    "testing"
)

func TestParallel(t *testing.T) {
    t.Parallel()
}

在并行执行的情况下,只有当那些被标记为并行的测试才会被并行执行,所以只有一个测试函数时是没有意义的。它应该在测试函数中第一个被调用,因为它会重置测试时间。

在并发情况下,同时运行的测试的数量默认取决于GOMAXPROCS,可通过-parallel n被指定。

$ go test -parallel 4

另外,一个可以实现并行的方法,尽管不是函数级力度,但却是包级粒度,类似执行go test p1 p2 p3,即同时执行多个测试包。在这种情况下,包会被先编译,并同时被执行。当然,这对于总的时间来说是有好处的。但它也可能会导致错误变得具有不可预测性。比如一些资源被多个包同时使用时。

为了保持可控性,-p标志可以用来指定编译和测试的并发数。当仓库中有多个测试包,并且每个包在不同的子目录中,一个可以执行所有包的命令是go test ./...,这包含当前目录和所有子目录。没有带-p标志执行时,总的运行时间应该接近于运行时间最长的包的时间(加上编译时间)。

$ go test -p 1 .

运行go test -p 1 ./...使编译和测试工具只能在一个包中执行时,总的时间应该接近于所有独立的包测试的时间加上编译的时间综合。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:JunChow520

查看原文:Go Test

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2477 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传