Go 单元测试和性能测试
测试对于互联网应用软件开发来说非常重要,它对软件可靠性保证具有重要意义,通过测试能够尽可能发现并改正软件中的错误,提高软件质量。
这里我们主要讲解Go语言如何实现单元测试和性能测试。
go语言中自带有一个轻量级的测试框架testing和自带的go test命令来实现单元测试和性能测试,testing框架和其他语言中的测试框架类似,你可以基于这个框架写针对相应函数的测试用例,也可以基于该框架写相应的压力测试用例,那么接下来让我们一一来看一下怎么写。
单元测试
创建目录test,在目录下创建add.go、add_test.go两个文件,add_test.go为单元测试文件。
add_test.go
package test
import "testing"
func TestAdd(t *testing.T) {
sum := Add(1, 2)
if sum == 3 {
t.Log("the result is ok")
} else {
t.Fatal("the result is wrong")
}
}
func TestAdd1(t *testing.T) {
t.Error("the result is error")
}
add.go
package test
func Add(a, b int) int {
return a + b
}
然后在项目目录下运行go test -v
就可以看到测试结果了
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
add_test.go:8: the result is ok
=== RUN TestAdd1
--- FAIL: TestAdd1 (0.00s)
add_test.go:14: the result is error
FAIL
exit status 1
FAIL _/D_/gopath/src/ados/test 0.419s
如果看到PASS字样证明测试通过,FAIL字样表示测试失败。
使用testing库的测试框架需要遵循以下几个规则如下:
- 文件名必须是
_test.go
结尾的,这样在执行go test
的时候才会执行到相应的代码 - 你必须import
testing
这个包 - 所有的测试用例函数必须是
Test
开头 - 测试用例会按照源代码中写的顺序依次执行
- 测试函数
TestXxx()
的参数是testing.T
,我们可以使用该类型来记录错误或者是测试状态 - 测试格式:
func TestXxx (t *testing.T)
,Xxx
部分可以为任意的字母数字的组合,但是首字母不能是小写字母[a-z],例如Testintdiv
是错误的函数名。 - 函数中通过调用
testing.T
的Error
,Errorf
,FailNow
,Fatal
,FatalIf
方法,说明测试不通过,调用Log
方法用来记录测试的信息。
性能测试或压力测试
压力测试用来检测函数(方法)的性能,和编写单元功能测试的方法类似,此处不再赘述,但需要注意以下几点:
- 压力测试用例必须遵循如下格式,其中XXX可以是任意字母数字的组合,但是首字母不能是小写字母 func BenchmarkXXX(b *testing.B) { ... }
-
go test
不会默认执行压力测试的函数,如果要执行压力测试需要带上参数-test.bench
,语法:-test.bench="test_name_regex"
,例如go test -test.bench=".*"
表示测试全部的压力测试函数 - 在压力测试用例中,请记得在循环体内使用
testing.B.N
,以使测试可以正常的运行 - 文件名也必须以
_test.go
结尾
在test目录下创建 reflect_test.go
package test
import (
"reflect"
"testing"
)
type Student struct {
Name string
Age int
Class string
Score int
}
func BenchmarkReflect_New(b *testing.B) {
var s *Student
sv := reflect.TypeOf(Student{})
b.ResetTimer()
for i := 0; i < b.N; i++ {
sn := reflect.New(sv)
s, _ = sn.Interface().(*Student)
}
_ = s
}
func BenchmarkDirect_New(b *testing.B) {
var s *Student
b.ResetTimer()
for i := 0; i < b.N; i++ {
s = new(Student)
}
_ = s
}
func BenchmarkReflect_Set(b *testing.B) {
var s *Student
sv := reflect.TypeOf(Student{})
b.ResetTimer()
for i := 0; i < b.N; i++ {
sn := reflect.New(sv)
s = sn.Interface().(*Student)
s.Name = "Jerry"
s.Age = 18
s.Class = "20005"
s.Score = 100
}
}
func BenchmarkReflect_SetFieldByName(b *testing.B) {
sv := reflect.TypeOf(Student{})
b.ResetTimer()
for i := 0; i < b.N; i++ {
sn := reflect.New(sv).Elem()
sn.FieldByName("Name").SetString("Jerry")
sn.FieldByName("Age").SetInt(18)
sn.FieldByName("Class").SetString("20005")
sn.FieldByName("Score").SetInt(100)
}
}
func BenchmarkReflect_SetFieldByIndex(b *testing.B) {
sv := reflect.TypeOf(Student{})
b.ResetTimer()
for i := 0; i < b.N; i++ {
sn := reflect.New(sv).Elem()
sn.Field(0).SetString("Jerry")
sn.Field(1).SetInt(18)
sn.Field(2).SetString("20005")
sn.Field(3).SetInt(100)
}
}
func BenchmarkDirect_Set(b *testing.B) {
var s *Student
b.ResetTimer()
for i := 0; i < b.N; i++ {
s = new(Student)
s.Name = "Jerry"
s.Age = 18
s.Class = "20005"
s.Score = 100
}
}
在test目录下,执行:
go test reflect_test.go -test.bench=".*"
结果如下
goos: windows
goarch: amd64
BenchmarkReflect_New-4 20000000 84.9 ns/op
BenchmarkDirect_New-4 30000000 50.6 ns/op
BenchmarkReflect_Set-4 20000000 89.9 ns/op
BenchmarkReflect_SetFieldByName-4 3000000 552 ns/op
BenchmarkReflect_SetFieldByIndex-4 10000000 132 ns/op
BenchmarkDirect_Set-4 30000000 53.0 ns/op
PASS
ok command-line-arguments 10.982s
上面的结果显示我们没有执行任何TestXXX
的单元测试函数,显示的结果只执行了压力测试函数,以第三行为例
BenchmarkReflect_New 函数执行了20000000次,每次的执行平均时间是84.9纳秒。最后一行 command-line-arguments 10.982s,代表总的执行时间为 10.982s。
如果只想对某个函数测试,以BenchmarkReflect_New 为例,执行命令
go test reflect_test.go -test.bench="BenchmarkReflect_New"
结果为:
goos: windows
goarch: amd64
BenchmarkReflect_New-4 20000000 84.9 ns/op
PASS
ok command-line-arguments 2.490s
如果测试整个目录下的所有测试执行:
go test -test.bench=".*"
如果想显示内存分配的次数和大小添加 -benchmem
go test reflect_test.go -benchmem -test.bench=".*"
goos: windows
goarch: amd64
BenchmarkReflect_New-4 20000000 88.3 ns/op 48 B/op 1 allocs/op
BenchmarkDirect_New-4 30000000 53.8 ns/op 48 B/op 1 allocs/op
BenchmarkReflect_Set-4 20000000 90.9 ns/op 48 B/op 1 allocs/op
BenchmarkReflect_SetFieldByName-4 3000000 564 ns/op 80 B/op 5 allocs/op
BenchmarkReflect_SetFieldByIndex-4 10000000 135 ns/op 48 B/op 1 allocs/op
BenchmarkDirect_Set-4 30000000 52.4 ns/op 48 B/op 1 allocs/op
PASS
ok command-line-arguments 12.955s
后两列代表分配的内存大小和次数(48 B/op 1 allocs/op)
推荐gotests
它是编写Go测试的一个Golang命令行工具,可以根据目标源文件的函数和方法签名生成表驱动的测试。将自动导入测试文件中的任何新依赖项。
参考:
https://studygolang.com/stati...
https://www.cnblogs.com/yjf51...
- 基于CPPN与GAN+VAE生成高分辨率图像
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(54)-工作流设计-所有流程监控
- (收藏)搭建.NET Framework 3.0开发环境 及SharePoint 2007/WSS 3环境
- WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(48)-工作流设计-起草新申请
- 把windows2003“搬”到手机上。
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(53)-工作流设计-我的批阅
- ASP.NET MVC5+EF6+EasyUI 后台管理系统--任务调度系统解析
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(52)-美化EasyUI皮肤和图标
- ASP.NET MVC5+EF6+EasyUI 后台管理系统--系统权限全套完整图
- 互联网时代的产品升级和用户反馈故事
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(50)-Easyui 扁平化皮肤
- 微信终于做了它最擅长的业务,市场已经轰动
- Bootstrap Metronic 学习记录(二)菜单栏
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 6. k8s + jenkins 实现持续集成(完)
- 7. 复制k8s Node节点 并重新初始化k8s-nodes2节点 (k8s连载)
- 8.k8s连载--重新生成k8s token(kubeadm join报错及解决)
- 3. dcoker容器的命令
- 4. 镜像的原理
- 5.docker容器数据卷
- 6. Dockerfile详解
- 3.docker搭建一个博客平台
- 4. Docker 私有仓库搭建
- 抓包工具Charles
- 5.docker仓库--harbor
- 短视频程序源码,实现选中可拖拽功能
- 6.docker的网络通讯
- 1.1 jvm核心类加载器--jdk源码剖析
- 1.2 双亲委派机制及其原理