go进阶--测试
Go语言提供了go test 命令行工具,使用该工具可以很方便的进行测试。
不仅Go语言源码中大量使用go test,在各种开源框架中的应用也极为普遍。
目前go test支持的测试类型有:
- 单元测试
- 性能测试
- 示例测试
1.单元测试
1.1项目结构
项目中单元测试的结构如下:
[GoExpert]
|--[src]
|--[gotest]
|--unit.go
|--unit_test.go
其中,util.go为源代码文件,unit_test.go为测试文件。要保证测试文件以_test.go结尾。
1.2 源代码文件
源代码文件unit.go中包含一个Add()方法,如下所示:
package gotest
// Add 方法用于演示go test使用
func Add(a int, b int) int {
return a + b
}
Add()方法仅提供两数加法,实际项目中不可能出现类似的方法,此处仅供单元测试示例。
1.3 单元测试
测试文件unit_test.go中包含一个测试方法TestAdd(),如下所示:
package gotest_test
import (
"testing"
"gotest"
)
func TestAdd(t *testing.T) {
var a = 1
var b = 2
var expected = 3
actual := gotest.Add(a, b)
if actual != expected {
t.Errorf("Add(%d, %d) = %d; expected: %d", a, b, actual, expected)
}
}
通过package语句可以看到,测试文件属于gotest_test包,测试文件也可以跟源文件在同一个包,但常见的做法是创建一个包专用于测试,这样可以使测试文件和源文件隔离。GO源代码以及其他知名的开源框架通常会创建测试包,而且规则是在原包名上加上”_test”。
测试函数命名规则为TestXxx,其中“Test”为单元测试的固定开头,go test只会执行以此为开头的方法。紧跟“Test”是以首字母大写的单词,用于识别待测试函数。
测试函数参数并不是必须要使用的,但”testing.T”提供了丰富的方法帮助控制测试流程。
t.Errorf()用于标记测试失败,标记失败还有几个方法,在介绍testing.T结构时再详细介绍。
1.4 执行测试
命令行下,使用go test命令即可启动单元测试,如下所示:
$ go test
PASS
ok gotest 0.378s
# 还可以使用交互的方式启动
$ go test -v unit_test.go
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
unit_test.go:8: 3
PASS
ok command-line-arguments 0.004s
通过打印可知,测试通过,花费时间为0.378s。
1.5 单元测试总结
- 测试文件名必须以
_test.go结尾; - 测试函数名必须以
TestXxx开始; - 命令行下使用
go test即可启动测试;
2.性能测试
2.1 项目结构
通常性能测试也称 基准测试, 目录结构如下
[GoExpert]
|--[src]
|--[gotest]
|--benchmark.go
|--benchmark_test.go
其中benchmark.go为源代码文件,benchmark_test.go为测试文件。
2.2 源码文件
源代码文件benchmark.go中包含MakeSliceWithoutAlloc()和MakeSliceWithPreAlloc()两个方法,如下所示:
package gotest
// MakeSliceWithPreAlloc 不预分配
func MakeSliceWithoutAlloc() []int {
var newSlice []int
for i := 0; i < 100000; i++ {
newSlice = append(newSlice, i)
}
return newSlice
}
// MakeSliceWithPreAlloc 通过预分配Slice的存储空间构造
func MakeSliceWithPreAlloc() []int {
var newSlice []int
newSlice = make([]int, 0, 100000)
for i := 0; i < 100000; i++ {
newSlice = append(newSlice, i)
}
return newSlice
}
两个函数分别用来测试切片预分配内存和不分配内存的性能问题。
两个方法都会构造一个容量为100000的切片,所不同的是MakeSliceWithPreAlloc()会预先分配内存,而MakeSliceWithoutAlloc()不预先分配内存,二者理论上存在性能差异,本次就来测试一下二者的性能差异。
2.3 测试文件
测试文件benchmark_test.go中包含两个测试方法,用于测试源代码中两个方法的性能,测试文件如下所示:
package gotest_test
import (
"testing"
"gotest"
)
func BenchmarkMakeSliceWithoutAlloc(b *testing.B) {
for i := 0; i < b.N; i++ {
gotest.MakeSliceWithoutAlloc()
}
}
func BenchmarkMakeSliceWithPreAlloc(b *testing.B) {
for i := 0; i < b.N; i++ {
gotest.MakeSliceWithPreAlloc()
}
}
性能测试函数命名规则为BenchmarkXxx,其中Xxx为自定义的标识,需要以大写字母开始,通常为待测函数。
testing.B提供了一系列的用于辅助性能测试的方法或成员,比如本例中的b.N表示循环执行的次数,而N值不用程序员特别关心,按照官方说法,N值是动态调整的,直到可靠地算出程序执行时间后才会停止,具体执行次数会在执行结束后打印出来。
2.4 执行测试
命令行下,使用go test -bench=.命令即可启动性能测试,如下所示:
$ go test -bench=.
BenchmarkMakeSliceWithoutAlloc-4 2000 1103822 ns/op
BenchmarkMakeSliceWithPreAlloc-4 5000 328944 ns/op
PASS
ok gotest 4.445s
# 可以指定某个测试文件中运行性能测试
$ go test -v -bench=. benchmark_test.go
其中-bench为go test的flag,该flag指示go test进行性能测试,即执行测试文件中符合”BenchmarkXxx”规则的方法。
紧跟flag的为flag的参数,本例表示执行当前所有的性能测试。
通过输出可以直观的看出,BenchmarkMakeSliceWithoutAlloc执行了2000次,平均每次1103822纳秒,BenchmarkMakeSliceWithPreAlloc执行了5000次,平均每次328944纳秒。
从测试结果上看,虽然构造切片很快,但通过给切片预分配内存,性能还可以进一步提升,符合预期。关于原理分析,请参考Slice相关章节。
2.5 是如何工作的
benchmark 用例的参数 b *testing.B,有个属性 b.N 表示这个用例需要运行的次数。b.N 对于每个用例都是不一样的。
那这个值是如何决定的呢?b.N 从 1 开始,如果该用例能够在 1s 内完成,b.N 的值便会增加,再次执行。b.N 的值大概以 1, 2, 3, 5, 10, 20, 30, 50, 100 这样的序列递增,越到后面,增加得越快。
2.6 性能测试总结
- 文件名必须以
_test.go结尾; - 函数名必须以
BenchmarkXxx开始; - 使用命令
go test -bench=.即可开始性能测试;
3. 示例测试(example test)
示例测试平时很少用到,但是在很多流行的开源项目中能经常见到,它的目的是指导开发人员如何通过示例测试代码,衍生出对应功能的代码
3.1 项目结构
项目结构如下
[GoExpert]
|--[src]
|--[gotest]
|--example.go
|--example_test.go
3.2 源码文件
源代码文件example.go中包含SayHello()、SayGoodbye()``PrintNames()三个方法,如下所示:
package gotest
import "fmt"
// SayHello 打印一行字符串
func SayHello() {
fmt.Println("Hello World")
}
// SayGoodbye 打印两行字符串
func SayGoodbye() {
fmt.Println("Hello,")
fmt.Println("goodbye")
}
// PrintNames 打印学生姓名
func PrintNames() {
students := make(map[int]string, 4)
students[1] = "Jim"
students[2] = "Bob"
students[3] = "Tom"
students[4] = "Sue"
for _, value := range students {
fmt.Println(value)
}
}
这几个方法打印内容略有不同,分别代表一种典型的场景:
- SayHello():只有一行打印输出
- SayGoodbye():有两行打印输出
- PrintNames():有多行打印输出,且由于Map数据结构的原因,多行打印次序是随机的。
3.3 测试文件
测试文件example_test.go中包含3个测试方法,于源代码文件中的3个方法一一对应,测试文件如下所示:
package gotest_test
import "gotest"
// 检测单行输出
func ExampleSayHello() {
gotest.SayHello()
// OutPut: Hello World
}
// 检测多行输出
func ExampleSayGoodbye() {
gotest.SayGoodbye()
// OutPut:
// Hello,
// goodbye
}
// 检测乱序输出
func ExamplePrintNames() {
gotest.PrintNames()
// Unordered output:
// Jim
// Bob
// Tom
// Sue
}
例子测试函数命名规则为ExampleXxx,其中Xxx为自定义的标识,通常为待测函数名称。
三个测试函数分别代表三种场景:
- ExampleSayHello(): 待测试函数只有一行输出,使用
// OutPut:检测。 - ExampleSayGoodbye():待测试函数有多行输出,使用
// OutPut:检测,其中期望值也是多行。 - ExamplePrintNames():待测试函数有多行输出,但输出次序不确定,使用
// Unordered output:检测。
注:字符串比较时会忽略前后的空白字符。
3.4 执行测试
命令行下,使用go test或go test example_test.go命令即可启动测试,如下所示:
3.5 总结
- 例子测试函数名需要以
Example开头; - 检测单行输出格式为
// Output: <期望字符串>; - 检测多行输出格式为
// Output: \ <期望字符串> \ <期望字符串>,每个期望字符串占一行; - 检测无序输出格式为
// Unordered output: \ <期望字符串> \ <期望字符串>,每个期望字符串占一行; - 测试字符串时会自动忽略字符串前后的空白字符;
- 如果测试函数中没有
Output标识,则该测试函数不会被执行; - 执行测试可以使用go test,此时该目录下的其他测试文件也会一并执行;
- 执行测试可以使用go test <xxx_test.go>,此时仅执行特定文件中的测试函数;
go进阶--测试的更多相关文章
- javascript进阶——测试和打包分发
建立一个面向对象的好的代码基础后,为了达到代码重用的目的,通过调试使用适当的测试用例进行测试尤为必要,之后就是打包分发的主题. 一.调试与测试 1.调试 Firebug:包含了错误控制台.调试器.DO ...
- JavaEE互联网轻量级框架整合开发(书籍)阅读笔记(7):装配SpringBean·依赖注入装配
一.依赖注入的三种方式 在实际环境中实现IoC容器的方式主要分为两大类,一类是依赖查找,依赖查找是通过资源定位,把对应的资源查找回来.另一类则是依赖注入.一般而言,依赖注入可分为3中方式: ...
- JavaEE互联网轻量级框架整合开发(书籍)阅读笔记(6):Spring IOC容器学习(概念、作用、Bean生命周期)
一.IOC控制反转概念 控制反转(IOC)是一种通过描述(在Java中可以是XML或者是注解)并通过第三方去生产或获取特定对象的方式. 主动创建模式,责任在于开发者,而在被动模式下,责任归于Ioc容器 ...
- Django 强大的ORM之增删改查
Django orm Django——orm进阶 测试Django中的orm 表结构: models.py class User(models.Model): name = model ...
- JavaScript入门经典(第7版)读书笔记
断断续续看了十来天,终于看完了,还是学到些东西,这本书还是不错的,各方面都有涉及. 补充了下之前不完善的JS 知识 笔记一般只记必要的东西. Table of Contents 1. JavaScr ...
- 【Web】CSS中的浮动float
CSS中的float 文章目录 CSS中的float 1.float浮动属性 2.float文字环绕图片 3.float浮动的真正原因以及副作用分析 4.清除浮动的四种解决方法 5.实际应用 导航效果 ...
- DLL劫持学习及复现
0x01 dll简介 在Windows系统中,为了节省内存和实现代码重用,微软在Windows操作系统中实现了一种共享函数库的方式.这就是DLL(Dynamic Link Library),即动态链接 ...
- python3使用迭代生成器yield减少内存占用
技术背景 在python编码中for循环处理任务时,会将所有的待遍历参量加载到内存中.其实这本没有必要,因为这些参量很有可能是一次性使用的,甚至很多场景下这些参量是不需要同时存储在内存中的,这时候就会 ...
- 聊聊UI自动化的PageObject设计模式
当我们开发UI自动化测试用例时,需要引用页面中的元素(数据)才能够进行点击(动作)并显示出页面内容.如果我们开发的用例是直接对HTML元素进行操作,则这样的用例无法"应对"页面中U ...
随机推荐
- Github上比较全的学习路线
github地址:https://github.com/kamranahmedse/developer-roadmap/blob/master/translations/chinese
- Python如何将py文件打包成exe
安装pyinstaller 打开cmd窗口,输入pip install pyinstaller,命令行输出successfully表示成功. 生成exe文件 一.单个py文件 在py文件目录下,打开c ...
- intouch/ifix嵌入视频控件2(报警视频联动初步思路)
在项目中有朋友遇到类似的需求,ifix中嵌入视频,并实现报警与视频的联动功能.诸如,重要设备启动时,摄像头自动弹窗,并持续一段时间自动弹窗关掉:设备故障时,摄像头自动截图,录像一段时间存储:设备停止时 ...
- 比POSTMAN更好用!在国产接口调试工具APIPOST中使用Mock
APIPOST可以让你在没有后端程序的情况下能真实地返回接口数据,你可以用APIPOST实现项目初期纯前端的效果演示,也可以用APIPOST实现开发中的数据模拟从而实现前后端分离.在使用APIPOST ...
- csredis-in-asp.net core理论实战-哨兵模式-使用示例
csredis 开源地址 https://github.com/2881099/csredis 续上篇 csredis-in-asp.net core理论实战-主从配置.哨兵模式 示例源码 https ...
- noip模拟测试20
考试总结:这次考试,我非常真实地感觉到了自己能力的提高,具体来说,在之前的考试中,读完题之后我只会想到暴力的思路,甚至有的题连暴力都打不出来,但是这次在考场上我已经有了自己的一些想法,有了一个深入思考 ...
- 判断状态栏是否显示以及获取状态栏高度的方法,及工具类列子【续:及OnGlobalLayoutListener的利用】
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0731/1640.html 本篇博客是http://www.cnblogs.co ...
- 【Android】真机调试新姿势:无线连接
由于工作需要,需要无线连接手机调试,特意百度了一下 在进行Android开发时,一般我们都是用usb线把手机和电脑连接起来进行调试工作.但如果你觉得这样不够酷的话,可以尝试一下无线连接,颇简单,GO! ...
- linux命令打基础
目录 一.shell概述 二.linux命令分类 三.linux命令行 3.1 格式 3.2 编辑Linux命令行 四.Linux基础命令 4.1 pwd:查看当前的工作目录 4.2 cd:切换工作目 ...
- 指向结构的指针 struct结构名称 *结构指针变量名
//指向结构的指针 struct结构名称 *结构指针变量名 //(*结构指针变量名).成员变量名//结构指针变量->成员变量名 1 #include<stdio.h> 2 #incl ...