- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章深入理解Golang的单元测试和性能测试由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
前言 。
大家做开发的应该都知道,在开发程序中很重要的一点是测试,我们如何保证代码的质量,如何保证每个函数是可运行,运行结果是正确的,又如何保证写出来的代码性能是好的,我们知道单元测试的重点在于发现程序设计或实现的逻辑错误,使问题及早暴露,便于问题的定位解决,而性能测试的重点在于发现程序设计上的一些问题,让线上的程序能够在高并发的情况下还能保持稳定。本小节将带着这一连串的问题来讲解Go语言中如何来实现单元测试和性能测试.
go语言中自带有一个轻量级的测试框架testing和自带的go test命令来实现单元测试和性能测试,testing框架和其他语言中的测试框架类似,你可以基于这个框架写针对相应函数的测试用例,也可以基于该框架写相应的压力测试用例,那么接下来让我们一一来看一下怎么写.
如何编写测试用例 。
由于go test命令只能在一个相应的目录下执行所有文件,所以我们接下来新建一个项目目录gotest,这样我们所有的代码和测试代码都在这个目录下.
接下来我们在该目录下面创建两个文件:gotest.go和gotest_test.go 。
1、gotest.go:这个文件里面我们是创建了一个包,里面有一个函数实现了除法运算
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package gotest
import (
"errors"
)
func Division(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("除数不能为0")
}
return a / b, nil
}
|
2、gotest_test.go:这是我们的单元测试文件,但是记住下面的这些原则:
TestXxx()
的参数是testing.T
,我们可以使用该类型来记录错误或者是测试状态func TestXxx (t *testing.T)
,Xxx部分可以为任意的字母数字的组合,但是首字母不能是小写字母[a-z],例如Testintdiv是错误的函数名。testing.T
的Error, Errorf, FailNow, Fatal, FatalIf方法,说明测试不通过,调用Log方法用来记录测试的信息。下面是我们的测试用例的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package gotest
import (
"testing"
)
func Test_Division_1(t *testing.T) {
if i, e := Division(6, 2); i != 3 || e != nil { //try a unit test on function
t.Error("除法函数测试没通过") // 如果不是如预期的那么就报错
} else {
t.Log("第一个测试通过了") //记录一些你期望记录的信息
}
}
func Test_Division_2(t *testing.T) {
t.Error("就是不通过")
}
|
我们在项目目录下面执行go test,就会显示如下信息:
1
2
3
4
5
|
--- FAIL: Test_Division_2 (0.00 seconds)
gotest_test.go:16: 就是不通过
FAIL
exit status 1
FAIL gotest 0.013s
|
从这个结果显示测试没有通过,因为在第二个测试函数中我们写死了测试不通过的代码t.Error,那么我们的第一个函数执行的情况怎么样呢?默认情况下执行go test是不会显示测试通过的信息的,我们需要带上参数go test -v,这样就会显示如下信息:
1
2
3
4
5
6
7
8
9
|
=== RUN Test_Division_1
--- PASS: Test_Division_1 (0.00 seconds)
gotest_test.go:11: 第一个测试通过了
=== RUN Test_Division_2
--- FAIL: Test_Division_2 (0.00 seconds)
gotest_test.go:16: 就是不通过
FAIL
exit status 1
FAIL gotest 0.012s
|
上面的输出详细的展示了这个测试的过程,我们看到测试函数1Test_Division_1测试通过,而测试函数2Test_Division_2测试失败了,最后得出结论测试不通过.
接下来我们把测试函数2修改成如下代码:
1
2
3
4
5
6
7
|
func Test_Division_2(t *testing.T) {
if _, e := Division(6, 0); e == nil { //try a unit test on function
t.Error("Division did not work as expected.") // 如果不是如预期的那么就报错
} else {
t.Log("one test passed.", e) //记录一些你期望记录的信息
}
}
|
然后我们执行go test -v,就显示如下信息,测试通过了:
1
2
3
4
5
6
7
8
|
=== RUN Test_Division_1
--- PASS: Test_Division_1 (0.00 seconds)
gotest_test.go:11: 第一个测试通过了
=== RUN Test_Division_2
--- PASS: Test_Division_2 (0.00 seconds)
gotest_test.go:20: one test passed. 除数不能为0
PASS
ok gotest 0.013s
|
如何编写压力测试 。
压力测试用来检测函数(方法)的性能,和编写单元功能测试的方法类似,此处不再赘述,但需要注意以下几点:
1
|
func BenchmarkXXX(b *testing.B) { ... }
|
-test.bench
,语法: -test.bench="test_name_regex",
例如go test -test.bench=".*"
表示测试全部的压力测试函数testing.B.N
,以使测试可以正常的运行下面我们新建一个压力测试文件webbench_test.go,代码如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package gotest
import (
"testing"
)
func Benchmark_Division(b *testing.B) {
for i := 0; i < b.N; i++ { //use b.N for looping
Division(4, 5)
}
}
func Benchmark_TimeConsumingFunction(b *testing.B) {
b.StopTimer() //调用该函数停止压力测试的时间计数
//做一些初始化的工作,例如读取文件数据,数据库连接之类的,
//这样这些时间不影响我们测试函数本身的性能
b.StartTimer() //重新开始时间
for i := 0; i < b.N; i++ {
Division(4, 5)
}
}
|
我们执行命令go test -test.bench=".*" ,可以看到如下结果:
1
2
3
4
|
PASS
Benchmark_Division 500000000 7.76 ns/op
Benchmark_TimeConsumingFunction 500000000 7.80 ns/op
ok gotest 9.364s
|
上面的结果显示我们没有执行任何TestXXX的单元测试函数,显示的结果只执行了压力测试函数,第一条显示了Benchmark_Division执行了500000000次,每次的执行平均时间是7.76纳秒,第二条显示了Benchmark_TimeConsumingFunction执行了500000000,每次的平均执行时间是7.80纳秒。最后一条显示总共的执行时间.
我们执行命令go test -test.bench=".*" -count=5,可以看到如下结果: (使用-count可以指定执行多少次) 。
1
2
3
4
5
6
7
8
9
10
11
12
|
PASS
Benchmark_Division-2 300000000 4.60 ns/op
Benchmark_Division-2 300000000 4.57 ns/op
Benchmark_Division-2 300000000 4.63 ns/op
Benchmark_Division-2 300000000 4.60 ns/op
Benchmark_Division-2 300000000 4.63 ns/op
Benchmark_TimeConsumingFunction-2 300000000 4.64 ns/op
Benchmark_TimeConsumingFunction-2 300000000 4.61 ns/op
Benchmark_TimeConsumingFunction-2 300000000 4.60 ns/op
Benchmark_TimeConsumingFunction-2 300000000 4.59 ns/op
Benchmark_TimeConsumingFunction-2 300000000 4.60 ns/op
ok _/home/diego/GoWork/src/app/testing 18.546s
|
go test -run=文件名字 -bench=bench名字 -cpuprofile=生产的cprofile文件名称 文件夹 。
例子:
testBenchMark下有个popcnt文件夹,popcnt中有文件popcunt_test.go 。
1
2
|
➜ testBenchMark ls
popcnt
|
popcunt_test.go的问价内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
ackage popcnt
import (
"testing"
)
const m1 = 0x5555555555555555
const m2 = 0x3333333333333333
const m4 = 0x0f0f0f0f0f0f0f0f
const h01 = 0x0101010101010101
func popcnt(x uint64) uint64 {
x -= (x >> 1) & m1
x = (x & m2) + ((x >> 2) & m2)
x = (x + (x >> 4)) & m4
return (x * h01) >> 56
}
func BenchmarkPopcnt(b *testing.B) {
for i := 0; i < b.N; i++ {
x := i
x -= (x >> 1) & m1
x = (x & m2) + ((x >> 2) & m2)
x = (x + (x >> 4)) & m4
_ = (x * h01) >> 56
}
}
|
然后运行go test -bench=".*" -cpuprofile=cpu.profile ./popcnt 。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
➜ testBenchMark go test -bench=".*" -cpuprofile=cpu.profile ./popcnt
testing: warning: no tests to run
PASS
BenchmarkPopcnt-8 1000000000 2.01 ns/op
ok app/testBenchMark/popcnt 2.219s
➜ testBenchMark ll
total 6704
drwxr-xr-x 5 diego staff 170 5 6 13:57 .
drwxr-xr-x 3 diego staff 102 5 6 11:12 ..
-rw-r--r-- 1 diego staff 5200 5 6 13:57 cpu.profile
drwxr-xr-x 4 diego staff 136 5 6 11:47 popcnt
-rwxr-xr-x 1 diego staff 3424176 5 6 13:57 popcnt.test
➜ testBenchMark
|
生产 cpu.profile问价和popcnt.test 文件 。
1
2
3
4
5
6
7
8
|
➜ testBenchMark ll
total 6704
drwxr-xr-x 5 diego staff 170 5 6 13:57 .
drwxr-xr-x 3 diego staff 102 5 6 11:12 ..
-rw-r--r-- 1 diego staff 5200 5 6 13:57 cpu.profile
drwxr-xr-x 3 diego staff 102 5 6 14:01 popcnt
-rwxr-xr-x 1 diego staff 3424176 5 6 13:57 popcnt.test
➜ testBenchMark
|
1
|
go tool pprof popcnt.test cpu.profile 进入交互模式
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
➜ testBenchMark go tool pprof popcnt.test cpu.profile
Entering interactive mode (type "help" for commands)
(pprof) top
1880ms of 1880ms total ( 100%)
flat flat% sum% cum cum%
1790ms 95.21% 95.21% 1790ms 95.21% app/testBenchMark/popcnt.BenchmarkPopcnt
90ms 4.79% 100% 90ms 4.79% runtime.usleep
0 0% 100% 1790ms 95.21% runtime.goexit
0 0% 100% 90ms 4.79% runtime.mstart
0 0% 100% 90ms 4.79% runtime.mstart1
0 0% 100% 90ms 4.79% runtime.sysmon
0 0% 100% 1790ms 95.21% testing.(*B).launch
0 0% 100% 1790ms 95.21% testing.(*B).runN
(pprof)
|
go tool pprof --web popcnt.test cpu.profile 进入web模式 。
1
|
$ go tool pprof --text mybin http://myserver:6060:/debug/pprof/profile
|
这有几个可用的输出类型,最有用的几个为: --text,--web 和 --list 。运行 go tool pprof 来得到最完整的列表.
下面分享一点go test的参数解读。来源 。
格式形如:
go test [-c] [-i] [build flags] [packages] [flags for test binary] 。
参数解读:
-c : 编译go test成为可执行的二进制文件,但是不运行测试.
-i : 安装测试包依赖的package,但是不运行测试.
关于build flags,调用go help build,这些是编译运行过程中需要使用到的参数,一般设置为空 。
关于packages,调用go help packages,这些是关于包的管理,一般设置为空 。
关于flags for test binary,调用go help testflag,这些是go test过程中经常使用到的参数 。
-test.v : 是否输出全部的单元测试用例(不管成功或者失败),默认没有加上,所以只输出失败的单元测试用例.
-test.run pattern: 只跑哪些单元测试用例 。
-test.bench patten: 只跑那些性能测试用例 。
-test.benchmem : 是否在性能测试的时候输出内存情况 。
-test.benchtime t : 性能测试运行的时间,默认是1s 。
-test.cpuprofile cpu.out : 是否输出cpu性能分析文件 。
-test.memprofile mem.out : 是否输出内存性能分析文件 。
-test.blockprofile block.out : 是否输出内部goroutine阻塞的性能分析文件 。
-test.memprofilerate n : 内存性能分析的时候有一个分配了多少的时候才打点记录的问题。这个参数就是设置打点的内存分配间隔,也就是profile中一个sample代表的内存大小。默认是设置为512 * 1024的。如果你将它设置为1,则每分配一个内存块就会在profile中有个打点,那么生成的profile的sample就会非常多。如果你设置为0,那就是不做打点了.
你可以通过设置memprofilerate=1和GOGC=off来关闭内存回收,并且对每个内存块的分配进行观察.
-test.blockprofilerate n: 基本同上,控制的是goroutine阻塞时候打点的纳秒数。默认不设置就相当于-test.blockprofilerate=1,每一纳秒都打点记录一下 。
-test.parallel n : 性能测试的程序并行cpu数,默认等于GOMAXPROCS.
-test.timeout t : 如果测试用例运行时间超过t,则抛出panic 。
-test.cpu 1,2,4 : 程序运行在哪些CPU上面,使用二进制的1所在位代表,和nginx的nginx_worker_cpu_affinity是一个道理 。
-test.short : 将那些运行时间较长的测试用例运行时间缩短 。
总结 。
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对我的支持.
原文链接:http://blog.csdn.net/shenlanzifa/article/details/51451814 。
最后此篇关于深入理解Golang的单元测试和性能测试的文章就讲到这里了,如果你想了解更多关于深入理解Golang的单元测试和性能测试的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在尝试运行这段代码,用随机数替换字符串中的一个字符: //Get the position between 0 and the length of the string-1 to insert
我有一个包含 3 个位置的数组,假设它的所有位置都是数字 5。 [5 5 5] 我怎样才能以保持 555 的方式将它传递给 var?就像这样。 n:= 555 最佳答案 与使用任何其他语言的方式相同:
我使用 go dep 工具版本 v0.4.1,现在当我运行 dep init 时它会按预期创建 2 个文件,当我打开 gopkg.lock 我发现例如以下内容 [[projects]] name
我正在制作学习联系申请。我有一个 NewContact()。 // Contact - defines the fields of an entire Contact type Contact str
我一直在尝试使用该模块: https://godoc.org/github.com/hirochachacha/go-smb2#RemoteFile.ReadAt 为了在 Windows 机器上对我的
我需要在 golang 中编译 golang 中的程序。有没有不使用 exec.Command("go","build") 的原生形式? 最佳答案 不幸的是,我认为使用 exec.Command 是利
编写输出有效 go 代码的 go 应用程序可能最好使用内置的“go”包及其一些子包(“go/ast”、“go/token”、“go/printer”、等)。 要创建字符串文字表达式,您需要创建一个 a
我正在尝试使用 Golang 和 gin 为我的 api 和前端编写代理。如果请求转到除“/api”之外的任何内容,我想代理到 svelte 服务器。如果出现“/api/something”,我想在
我偶然发现了这个博客:using go as a scripting language并尝试创建一个可用于运行 golang 脚本的自定义图像,即 FROM golang:1.15 RUN go ge
我刚开始接触golang,我需要从json字符串中获取数据。 {"data" : ["2016-06-21","2016-06-22","2016-06-25"], "sid" : "ab", "di
关闭。这个问题是opinion-based .它目前不接受答案。 想要改进这个问题? 更新问题,以便 editing this post 可以用事实和引用来回答它. 关闭 3 年前。 Improve
我是 goland 的新手,试图在我的第一个项目中使用它。我注意到在 goland 中它没有显示通过容器引入的相同 golang SDK。 这是我的 Dockerfile: FROM golang:1
我正在试用 golang-neo4j-bolt-driver 包 github.com/johnnadratowski/golang-neo4j-bolt-driver 我已经导入了包并正在使用创建新
如果我安装了Go发行版软件包,则会在/usr/lib/golang/pkg中看到很多文件,在/usr/lib/golang/src中看到非常相似的文件集。这两组之间有什么关系? pkg是从src中的源
我发现 golang 上下文对于在客户端-服务器请求范围内取消服务器的处理很有用。 我可以使用 http.Request.WithContext 方法发出带有上下文的 http 请求,但是如果客户端不
我正在尝试将一个 golang 数组(还有 slice、struct 等)放置到 HTML 中,这样当从 golang gin web 框架返回 HTML 时,我可以在 HTML 元素内容中使用数组元
目前正在使用这个 ffmpeg 命令编辑视频 ffmpeg -i "video1.ts" -c:v libx264 -crf 20 -c:a aac -strict -2 "video1-fix.ts
我需要从 play.golang.org 链接读取 golang 代码并保存到 .go 文件。我想知道 play.golang.org 是否有任何公共(public) API 支持。我用谷歌搜索但没有
我第一次使用 IntelliJ 的最新 (2014-01-03) Golang 插件。 通常,我的终端工作流程是 go build && ./executable -args=1 所以我试图创建一个启
这个问题只是在构建之间随机出现,现在甚至我们的生产 repo,几个月都没有改变,在构建时也会出现这个问题。我已经坚持了一段时间。它不会发生在我们的本地机器上,只有在使用 dockerfile 时才会发
我是一名优秀的程序员,十分优秀!