go test 测试用例那些事
go test命令,相信大家都不陌生,常见的情况会使用这个命令做单测试、基准测试和http测试。go test还是有很多flag 可以帮助我们做更多的分析,比如测试覆盖率,cpu分析,内存分析,也有很多第三方的库支持test,cpu和内存分析输出结果要配合pprof和go-torch来进行可视化显示,可以看一下之前的这篇帖子 golang 使用pprof和go-torch做性能分析,这篇帖子总结一下go test的一些常用方式和推荐一些很棒的第三方库。
go test文件命名是以_test.go为缀。例如userInfo_test.go。在github上写了一个小的项目,包含常见的测试方法: https://github.com/lpxxn/gotest 。app1里是基本的测试方法。app2里包含了一些第三方的库辅助我们更方便的测试。 大家可以下载来来 go test一下试试
测试函数以Test或者Bench为前缀开始,如:
func TestXXXXXXX(t *testing.T)
func BenchXXXXXX(b *testing.B)
func TestMain(m *testing.M)
看一下testing.T和testing.B者有组合 common
type T struct {
common
isParallel bool
context *testContext // For running tests and subtests.
}
type B struct {
common
importPath string // import path of the package containing the benchmark
context *benchContext
N int
previousN int // number of iterations in the previous run
previousDuration time.Duration // total duration of the previous run
benchFunc func(b *B)
benchTime time.Duration
bytes int64
missingBytes bool // one of the subbenchmarks does not have bytes set.
timerOn bool
showAllocResult bool
result BenchmarkResult
parallelism int // RunParallel creates parallelism*GOMAXPROCS goroutines
// The initial states of memStats.Mallocs and memStats.TotalAlloc.
startAllocs uint64
startBytes uint64
// The net total of this test after being run.
netAllocs uint64
netBytes uint64
}
common包含了T和B的所有公共方法,常见的比如Log()日志信息,Error() 错误信息,Fail()致命错误等方法,
TestMain(*testing.M)方法有些特殊,在一个包内只能有一个TestMain方法。这个方法会在测试方法运行前调用,相当于main()方法。我们可以在这个方法内做一些初始化数据操作等。看一下testing.M结构体
// M is a type passed to a TestMain function to run the actual tests.
type M struct {
deps testDeps
tests []InternalTest
benchmarks []InternalBenchmark
examples []InternalExample timer *time.Timer
afterOnce sync.Once numRun int
}
专为TestMain准备
先以app1来对基本的test进行解说,app1的项目结构为。

具体的代码大家看一下就好,都是一些特别简单的方法。
测试指定函数
简单的测试方法
func TestNewUserInfo(t *testing.T) {
u := NewUserInfo()
if len(u.Name) == {
t.Error("name is empty")
}
}
得到新创建的user信息,检查name是否为空,如果为空则错误。
-run 后面的参数是正则,所有匹配这正则的方法都会被运行,比如测试所有包含user(不区分大小写)的测试方法:
go test -v -run="(?i)user"

-v 是用于输出所有Log的信息
也可以指写具体的方法名,只要包含这些名称的测试方法就会运行,如果要测试多个方法,名称用"|"分开
go test -v -run=TestGetOrderList
go test -v -run="TestGetOrderList|TestNewUserInfo"

执行的结果不用我多说,运行是否通过,最后还有运行的时长,方法实在在简单了,执行的太快只精确到2位,所以0.00。
测试指定文件
测试指定的_test.go文件,需要注意的是在同一个包下,需要把测试文件和源文件都写出来:
go test -v user_test.go user.go

测试文件夹内所有的test文件
直接在某个目录运行go test命令就会运行这个文件夹下所有的_test.go文件内的测试方法。
go test -v

如果文件夹里还包含文件夹,可以添加 "./..."来递归测试。
go test -v ./...

BenchMark 测试
benchMark通过一系列的性能指标来对我们的方法进行测试,比如cpu,内存。循环测试次数据等。
基本的命令是
go test -bench="."
-bench 类似于-run 可以跟正则表达式来控制执行的方法
测试方法
func BenchmarkUserInfoList(b *testing.B) {
b.StopTimer()
// do something
b.StartTimer()
for i := ; i < b.N; i++ {
// pretend delay
//time.Sleep(time.Millisecond * 500)
userInfoList := UserInfoList()
if len(userInfoList) != {
b.Error("userInfoList is empty")
}
}
}
返回的结果

benchmark方法名加当前测试cpu内核的数量,这个可以通过-cpu 来设置数量,10000是执行的次数,就是代码中的b.N 171679 ns/op 每次操作的耗时。
可以通过flag benchtime来控制执行时长
go test -bench="." -cpu= -benchtime=5s
-benchmem 用于显示内存的分配情况

808 B/op 表示每一调用需要808个字节, 35 allocs/op表示每一次调用有35次内存分配
当然还有很多flag 大家可以通过下面的命令查看官方文档
go help testflag
TestMain
一个包下面只能有一个TestMain方法。这个方法就和main()方法差不太多。他会在其他的Test方法之前执行,我们可以利用他做一些初始化数据操作,执行完后释放资源等操作。
例如我在TestMain里把当前的环境变量设置为dev。 然后加载配置,当然都是模拟的。
func TestMain(m *testing.M) {
os.Setenv(config.ENV_STR, config.ENV_DEV)
config.LoadConfig()
fmt.Printf("Env is %s\n", os.Getenv(config.ENV_STR))
fmt.Printf("config info %#v\n", config.ConfigInfo)
// do something... eg: init data
// ...
os.Exit(m.Run())
}
在执行所有的go test时会先执行TestMain

测试代码覆盖率
测试覆盖率就是运行我们的测试方法所有跑过的代码占全部代码的比例,比如我们跑一下user_test.go的所有测试方法,然后看一下覆盖率:
两个命令:
go test -v -coverprofile cover.out user_test.go user.go
go tool cover -html=cover.out -o cover.html
一个是执行测试,另一个是把输出的文件转换成html

用浏览器打开生成的html,绿颜色表示运行到的代码,红颜色表示没有运行到的代码,我的代码是全部都运行到了。

测试http
先来一个原生的http handler方法
func HandleNewUser(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
fmt.Printf("url parameter user name is %s\n", name)
say := r.FormValue("say")
fmt.Printf("req say:' %s '\n", say)
newUser := NewUserInfo()
jData, _ := json.Marshal(newUser)
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
w.Write(jData)
}
这个方法没有什么逻辑,在url中获取name参数,然后在post的form中获取say参数,再返回一个user的json。
再看一下测试方法
func TestHandleNewUser(t *testing.T) {
postBody := url.Values{}
postBody.Add("say", "hello world")
req := httptest.NewRequest(http.MethodPost, "http://localhost/createuser?name=linus", strings.NewReader(postBody.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
w := httptest.NewRecorder()
HandleNewUser(w, req)
if w.Code != http.StatusOK {
t.Error("new user api error")
}
if w.Body.Len() == {
t.Error(" response is empty")
}
user := &model.UserInfo{}
err := json.Unmarshal(w.Body.Bytes(), user)
if err != nil {
t.Error("response data error")
}
t.Logf("create user api response : %#v", user)
}
使用的是httptest包,先是创建了一个请求,url包含了name参数,body里有say参数。然后再通过NewRecorder创建一个responseWriter,把这两个参数传递给我们的handler,再测试有没有返回我们想要的执行结果。
如果你使用的web服务框架是gin。测试gin handler的代码我写在了app2里。有时间你可以看一下,大致的代码:
func TestNewUserInfo(t *testing.T) {
a := assert.New(t)
router := gin.New()
const path = "/newUserInfo"
router.POST(path, NewUserInfo)
body := url.Values{}
body.Set("say", "hello world")
rr, err := testutils.PostFormRequst(path + "?name=lp", router, body)
a.Nil(err)
user := &model.UserInfo{}
err = json.Unmarshal(rr.Body.Bytes(), user)
a.Nil(err)
a.NotEqual(user.Name, "")
a.NotEqual(user.Age, )
t.Logf("%#v\n", user)
}
推荐几个第三方的库
有几个我常用的第三方库给大家推荐一下,相关的测试代码都写到 app2_thirdlib里了
github.com/stretchr/testify
github.com/jarcoal/httpmock
testify里有assert相信有其他语言基础的同学一定知道他是做什么的,断言处理比如
a.Nil(err)
a.NotEqual(user.Name, "")
a.NotEqual(user.Age, )
如果判断的结果为false则测试失败。
httpmock这个好玩,假如我们的项目有请求其他项目的api调用,但是我们没有源码,只知道返回结果。但是我们进行test测试时,要请求这个api。httpmock就是做这个用的,他们拦住我们的http请求,然后返回我们预置的response。
func TestUserRoleList(t *testing.T) {
a := assert.New(t)
// mock http
testutils.HttpMockActivateNonDefault()
httpmock.RegisterNoResponder(
httpmock.NewStringResponder(http.StatusOK, fmt.Sprintf(`
[
{
"id": ,
"name": "a"
},
{
"id": ,
"name": "b"
},
{
"id": ,
"name": "c"
}
]
`)))
defer httpmock.DeactivateAndReset()
router := gin.New()
const path = "/userRoleList"
router.GET(path, UserRoleList)
rr, err := testutils.GetRequst(path, router)
a.Nil(err)
a.Equal(rr.Result().StatusCode, http.StatusOK)
roles := make([]model.UserRole, )
err = json.Unmarshal(rr.Body.Bytes(), &roles)
a.Nil(err)
a.NotEqual(len(roles), )
t.Logf("len of roles: %d\n", len(roles))
}
我的UserRoleList方法调用了其他项目的api。httpmock会把这个http请求拦住,上面我们提前写好的数组。
大家可以下载我的源码,然后 go test ./... 一下试试
go test 测试用例那些事的更多相关文章
- go test 测试用例那些事(二) mock
关于go的单元测试,之前有写过一篇帖子go test测试用例那些事,但是没有说go官方的库mock,很有必要单独说一下这个库,和他的实现原理. mock主要的功能是对接口的模拟,需要在写代码的时候定义 ...
- selenium项目--读取测试用例
读取测试用例 一直我们都没有考虑过读取测试用例的事,我们现在这样设计测试用例有两个好的点,在执行方法时,打印测试用例,方便知道执行的内容是什么,在报告展示时,把测试用例的结果展示出来 实现方案:目前我 ...
- 关于angularjS与jQuery框架的那些事
这篇文章主要介绍了jQuery和angularJS的区别浅析,本文着重讲解一个熟悉jQuery的程序员如何应对angularJS中的一些编程思想的转变吗,需要的朋友可以参考下 最近一直研究angula ...
- robotframework笔记3--如何编写好的测试用例使用机器人的框架
命名 测试套件的名称 之后,你可能应该描述你的名字. 名称是从文件或目录名自动创建: 扩展了. 强调了转换空间. 如果名称都是小写,大写的单词是. 名称可以是比较长的,但是太长的名字不方便 文件系 ...
- react.js 你应知道的9件事
React.js 初学者应该知道的 9 件事 本文假定你已经有了一下基本的概念.如果你不熟悉 component.props 或者 state 这些名词,你最好先去阅读下官方起步和手册.下面的代码 ...
- java程序员最不愿意看到的十件事
0.遍历结果集并构造对象如果你是个时髦的开发者而不是专业人员,显然你从某篇博客中读过有开发者遇到Hibernate的“性能问题”,因而认为ORM都不好,觉得手动编码“明显更好”.喜欢的话你当然可以用 ...
- 摘抄-----java codeReview要做的事
整洁的代码 清单项目 分类 使用可以表达实际意图(Intention-Revealing)的名称 有意义的名称 每一个概念只用一个词 有意义的名称 使用方案/问题领域名称 有意义的名称 类应该是比较小 ...
- 不愿看到Java开发者再做的10件事
William F. Buckley.Jr 曾经说过,“保守主义者是那些逆着历史潮流不断喊停的人,其他人都不愿意这么做或者对他们这么做显得没有耐性”.虽然我对此了解不多,但是每次看到有Java开发人员 ...
- 通用测试用例大全(转自——知了.Test)
为方便平时写测试用例,整理如下: 功能 条件 测试步骤 测试数据 预期结果 备注 搜索或查询 单独遍历各查询条件,测试按各查询条件是否都能够查询出相应的值. 查询出符合条件的记录 设置 ...
随机推荐
- day 39 mycql 数据库之约束
egon笔记: PRIMARY KEY (PK) 标识该字段为该表的主键,可以唯一的标识记录 UNIQUE KEY (UK) 标识该字段的值是唯一的 AUTO_INCREMENT 标识该字段的值自动增 ...
- 大数据及Hadoop的概述
一.大数据存储和计算的各种框架即工具 1.存储:HDFS:分布式文件系统 Hbase:分布式数据库系统 Kafka:分布式消息缓存系统 2.计算:Mapreduce:离线计算框架 stor ...
- C语言实现链栈
我自己写的代码部分: #include<stdio.h> #include<stdlib.h> #include<string.h> typedef struct ...
- Python并发复习3 - 多进程模块 multiprocessing
python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程.Python提供了非常好用的多进程包multiprocessing,只需要定 ...
- 使用ORM进行前后端数据交互
使用ORM进行数据交互 前期准备 必备知识:ORM操作,数据库多表操作.Django部分知识. 三张表:班级.老师.学生 一对多关系:班级与学生 多对多关系:班级与老师 #创建班级表 class Cl ...
- SpringMVC(二五) JSTL View
项目中使用JSTL,SpringMVC会把视图由InternalView转换为JstlView. 若使用Jstl的fmt标签,需要在SpringMVC的配置文件中配置国际化资源文件. 实现过程: 1. ...
- JS实现缓动动画效果
原理如下: 假设要从数值A变化到数值B,如果是线性运动,则每次移动距离是一样:如果是缓动,每次移动距离不一样.那如何才能不一样呢?很简单,按比例移动就可以. 例如:每次移动剩余距离的一半. 对吧,超容 ...
- Linux——awk命令解析
awk简介 awk其名称得自于它的创始人 Alfred Aho .Peter Weinberger 和 Brian Kernighan 姓氏的首个字母.实际上 AWK 的确拥有自己的语言: AWK 程 ...
- python网络编程(四)
TFTP客户端 1. TFTP协议介绍 TFTP(Trivial File Transfer Protocol,简单文件传输协议) 是TCP/IP协议族中的一个用来在客户端与服务器之间进行简单文件传输 ...
- 回文检测 [USACO Training Section 1.3]
题目描述 据说如果你给无限只母牛和无限台巨型便携式电脑(有非常大的键盘),那么母牛们会制造出世上最棒的回文.你的工作就是去寻找这些牛制造的奇观(最棒的回文). 在寻找回文时不用理睬那些标点符号.空格( ...