go-单元测试-基准测试

go-单元测试-基准测试


使用

  • 文件名后后缀为 _test

    • 一般把 测试文件 丢带被 测试的方法 同包名 下. (构建 main 程序执行时, 不会执行到 _test 文件中的 init 方法)
    • 单元测试, 要求函数名以 Test 开头, 参数为 *testing.T
    • 性能测试, 要求函数名以 Benchmark 开头, 参数为 *testing.B
    • Test /Benchmark 开头后面紧接着 _ 或 大写字母开头的单词, 如
      • 合法: TestRun (建议), Test_run
      • 不合法: Testrun
  • 待测试文件 cat.go

    1
    2
    3
    4
    5
    6
    7
    package cat

    import "log"

    func Run(speed int) {
    log.Printf("--- Run, speed:%+v\n", speed)
    }
  • 单元测试文件 cat_test.go

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package cat

    import (
    "testing"
    )

    func TestRun(t *testing.T) { // 单元测试, 要求函数名以 Test 开头, 参数为 *testing.T
    Run(111)
    }

    func BenchmarkTransport1(b *testing.B) { // 性能测试, 要求函数名以 Benchmark 开头, 参数为 *testing.B
    Run(222)
    }

vscode 中直接可以点击测试

goland 中右键点击测试


单元测试


命令

测试指定文件

cd 到测试文件目录下执行命令

1
2
3
4
5
6
7
8
9
10
11
12
f:\a_link_workspace\go\GoWinEnv_new\src\GoLab\test_uuid (master -> origin)
$ go test -v uuid_test.go
=== RUN TestUuid001
uid1: 655485b4-49da-41bf-b5bb-decb47254efc
uid2: 86474f94-1990-4738-bd9e-15a8a8b90d54
--- PASS: TestUuid001 (0.00s)
=== RUN TestUuid002
u1: 123e4567-e89b-12d3-a456-426655440000
u2: 28f6ff82-b570-4dd7-b1a3-27c7110d6b17
--- PASS: TestUuid002 (0.00s)
PASS
ok command-line-arguments 0.129s
  • $ go test -v : 测试所有文件
  • $ go test -v file1.go file2.go : 测试指定文件

测试指定方法

cd 到测试文件目录下执行命令

1
2
3
4
f:\a_link_workspace\go\GoWinEnv_Mars\src\mars\test\rpc (master -> origin)
$ go test -v -run ^Test_002$
=== RUN Test_002
2019/10/29 15:13:35 --- hello
  • ^Test_002$ 测试所有文件的所有匹配的方法
  • 可用参数
    • -timeout d : 默认 d 是 10m0s; 如果设为 0, 则表示不超时

测试结果 json 化
  • 输出 json 格式的结果, 在测试方法后面加上 -json 标记

    1
    2
    3
    4
    $ go test -v -timeout 0 -run Test_001 -json
    {"Time":"2020-11-03T10:35:42.1270576+08:00","Action":"run","Package":"go-lab/test_flag","Test":"Test_001"}
    ...
    {"Time":"2020-11-03T10:35:42.1758557+08:00","Action":"pass","Package":"go-lab/test_flag","Elapsed":0.389}

命令行参数
  • 方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    func Test_Args(t *testing.T) {
    if !flag.Parsed() {
    flag.Parse()
    }

    argList := flag.Args() // flag.Args() 返回 -args 后面的所有参数,以切片表示,每个元素代表一个参数
    for _, arg := range argList {
    fmt.Printf("--- arg: %s\n", arg)
    }
    }
直接执行测试方法
  • 命令, 在 测试方法后面加入 -args 标识及参数

    1
    2
    3
    4
    5
    6
    $ go test -v -timeout 0 -run Test_Args -args aaa bbb ccc
    === RUN Test_Args
    --- arg: aaa
    --- arg: bbb
    --- arg: ccc
    --- PASS: Test_Args (0.00s)
构建二进制后再执行测试方法
  • 命令

    1
    2
    3
    4
    5
    6
    7
    8
    $ go test -c // 构建 test_flag.test.exe 程序

    $ .\test_flag.test.exe -test.v -test.run Test_Args aaa bbb ccc
    === RUN Test_Args
    --- arg: aaa
    --- arg: bbb
    --- arg: ccc
    --- PASS: Test_Args (0.00s)

基准测试

命令

测试当前目录所有文件所有方法

go test -bench=. -benchtime=3s

测试指定文件指定方法

go test benchmark_test.go -bench=Benchmark_Cast -benchtime=3s

测试用例 benchmark_test.go 文件

1
2
3
4
5
6
7
8
9
10
func Benchmark_Cast(b *testing.B) {
b.ReportAllocs() // 在report中包含内存分配信息,例如结果是:
// Benchmark_Cast-3 1000000000 0.567 ns/op 0 B/op 0 allocs/op

var dgIns *CDog
for i := 0; i < b.N; i++ { // b.N, 次数
dgIns = dg.(*CDog)
}
_ = dgIns
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
F:\a_link_workspace\go\GoWinEnv_new\src\GoLab\test_unit_benchmark (master -> origin)
$ go test benchmark_test.go -bench=Benchmark_Cast -benchtime=3s -cpu=8 -count=2
2019/11/27 11:58:17 --- Run, speed:111
goos: windows
goarch: amd64
pkg: GoLab/test_unit_benchmark
// Benchmark 名字 - CPU 循环次数 每次执行耗时0.567纳秒 每次执行分配了0字节内存 每次执行分配了0次对象
Benchmark_Cast-8 1000000000 0.567 ns/op 0 B/op 0 allocs/op
Benchmark_Cast-8 1000000000 0.522 ns/op 0 B/op 0 allocs/op
PASS
// ok swap/lib 2.279s
ok GoLab/test_unit_benchmark 0.884s
  • -count=2 : 运行 2 次
  • -cpu=8 : The 8 后缀和用于运行次测试的 GOMAXPROCS 值有关。 与GOMAXPROCS一样,此数字默认为启动时Go进程可见的CPU数。 你可以使用-cpu标识更改此值,可以传入多个值以列表形式来运行基准测试。

Cpu Profile

生成 profile 文件
1
$ go test -run=xxx -bench=. -benchtime=3s -cpuprofile profile_cpu.out

xxx 就是 xxx, 不代表什么反方, xxx 就对了

该命令会跳过单元测试,执行所有benchmark,同时生成一个cpu性能描述文件.

查看 profile 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ go tool pprof app.test profile_cpu.out
app.test: open app.test: The system cannot find the file specified.
Fetched 1 source profiles out of 2
Type: cpu
Time: Dec 8, 2019 at 4:35pm (CST)
Duration: 1.04s, Total samples = 1.46s (140.85%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top10 // 输入 top10, 查看消耗最高的 10 个方法
Showing nodes accounting for 1.46s, 100% of 1.46s total
Showing top 10 nodes out of 21
flat flat% sum% cum cum%
0.82s 56.16% 56.16% 0.82s 56.16% testing.(*PB).Next
0.48s 32.88% 89.04% 0.48s 32.88% GoLab/test_unit_benchmark.Benchmark_Cast
0.13s 8.90% 97.95% 0.95s 65.07% GoLab/test_unit_benchmark.Benchmark_Parallel.func1
0.01s 0.68% 98.63% 0.01s 0.68% runtime.releasep
0.01s 0.68% 99.32% 0.01s 0.68% runtime.stdcall1
0.01s 0.68% 100% 0.01s 0.68% runtime.stdcall2
0 0% 100% 0.02s 1.37% runtime.entersyscallblock_handoff
0 0% 100% 0.01s 0.68% runtime.findrunnable
0 0% 100% 0.01s 0.68% runtime.goexit0
0 0% 100% 0.02s 1.37% runtime.handoffp
(pprof)

build 单元测试 可执行文件

有了这个就可以不用单独又写一个 main 包来构建 可执行文件, 直接构建 测试用例 并 执行

参考: https://deepzz.com/post/the-command-flag-of-go-test.html

比如: pc 环境下, 测试用例 所在 包名 为: test_http,

  1. 测试代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    package test_http

    import (
    "log"
    "testing"
    )

    func Test_fileSrv01(t *testing.T) {
    log.Printf("--- Test_fileSrv01\n")
    }

    func Test_fileSrv02(t *testing.T) {
    log.Printf("--- Test_fileSrv02\n")
    }
  2. cd 到 测试用例 所在目录, 执行命令: go test -c. 编译测试二进制文件为 [pkg].test,不运行测试。

    1
    2
    I:\workspace\go\go-lab\test_net\test_http (master -> origin)
    $ go test -c

    这里则会生成 test_http.test.exe

  3. 执行可执行文件里的测试用例, 执行命令:

    1
    2
    3
    $ .\test_http.test.exe -test.run ^Test_fileSrv02$
    2020/10/18 13:36:49 --- Test_fileSrv02
    PASS

执行带参数的可执行文件

  1. 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var (
    a = flag.Int("a", 0, "first number")
    b = flag.Int("b", 0, "second number")
    expected = flag.Int("expected", 0, "expected result")
    )

    func TestRummyFileSrv(t *testing.T) {
    flag.Parse()
    fmt.Printf("a: %d, b: %d\n", *a, *b)
    }
  2. 构建可执行文件

    1
    $ go test -c
  3. 传入参数并执行, -test.v -a=1 -b=2 就是传入参数的示例

    1
    2
    3
    4
    5
    $ .\test_game.test.exe -test.v -a=1 -b=2 -test.run ^TestRummyFileSrv
    === RUN TestRummyFileSrv
    a: 1, b: 2
    --- PASS: TestRummyFileSrv (0.00s)
    PASS