Go语言拥有一套单元测试和性能测试系统,仅需要添加很少的代码就可以快速测试一段需求代码。

go test 命令,会自动读取源码目录下面名为 *_test.go 的文件,生成并运行测试用的可执行文件。输出的信息类似

性能测试系统可以给出代码的性能数据,帮助测试者分析性能问题。

单元测试——测试和验证代码的框架

要开始一个单元测试,需要准备一个 go 源码文件,在命名文件时需要让文件必须以_test结尾。默认的情况下,go test 命令不需要任何的参数,它会自动把你源码包下面所有 test 文件测试完毕,当然你也可以带上参数。

这里介绍几个常用的参数:

  • -bench regexp 执行相应的 benchmarks,例如 -bench=.
  • -cover 开启测试覆盖率
  • -run regexp 只运行 regexp 匹配的函数,例如 -run=Array 那么就执行包含有 Array 开头的函数
  • -v 显示测试的详细命令

单元测试源码文件可以由多个测试用例组成,每个测试用例函数需要以Test为前缀,例如:

func TestXXX( t *testing.T )

  • 测试用例文件不会参与正常源码编译,不会被包含到可执行文件中。
  • 测试用例文件使用 go test 指令来执行,没有也不需要 main() 作为函数入口。所有在以_test结尾的源码内以Test开头的函数会自动被执行。
  • 测试用例可以不传入 *testing.T 参数。

helloworld 的测试代码(具体位置是./src/chapter11/gotest/helloworld_test.go):

本套教程所有源码下载地址:https://pan.baidu.com/s/1ORFVTOLEYYqDhRzeq0zIiQ    提取密码:hfyf

  1. package code11_3
  2. import "testing"
  3. func TestHelloWorld(t *testing.T) {
  4. t.Log("hello world")
  5. }

代码说明如下:

  • 第 5 行,单元测试文件 (*_test.go) 里的测试入口必须以 Test 开始,参数为 *testing.T 的函数。一个单元测试文件可以有多个测试入口。
  • 第 6 行,使用 testing 包的 T 结构提供的 Log() 方法打印字符串。

1) 单元测试命令行

单元测试使用 go test 命令启动,例如:

$ go test helloworld_test.go
ok command-line-arguments 0.003s
$ go test -v helloworld_test.go
=== RUN TestHelloWorld
--- PASS: TestHelloWorld (0.00s)
helloworld_test.go:8: hello world
PASS
ok command-line-arguments 0.004s

代码说明如下:

  • 第 1 行,在 go test 后跟 helloworld_test.go 文件,表示测试这个文件里的所有测试用例。
  • 第 2 行,显示测试结果,ok 表示测试通过,command-line-arguments 是测试用例需要用到的一个包名,0.003s 表示测试花费的时间。
  • 第 3 行,显示在附加参数中添加了-v,可以让测试时显示详细的流程。
  • 第 4 行,表示开始运行名叫 TestHelloWorld 的测试用例。
  • 第 5 行,表示已经运行完 TestHelloWorld 的测试用例,PASS 表示测试成功。
  • 第 6 行打印字符串 hello world。

2) 运行指定单元测试用例

go test 指定文件时默认执行文件内的所有测试用例。可以使用-run参数选择需要的测试用例单独执行,参考下面的代码。

一个文件包含多个测试用例(具体位置是./src/chapter11/gotest/select_test.go

  1. package code11_3
  2. import "testing"
  3. func TestA(t *testing.T) {
  4. t.Log("A")
  5. }
  6. func TestAK(t *testing.T) {
  7. t.Log("AK")
  8. }
  9. func TestB(t *testing.T) {
  10. t.Log("B")
  11. }
  12. func TestC(t *testing.T) {
  13. t.Log("C")
  14. }

这里指定 TestA 进行测试:

$ go test -v -run TestA select_test.go
=== RUN TestA
--- PASS: TestA (0.00s)
select_test.go:6: A
=== RUN TestAK
--- PASS: TestAK (0.00s)
select_test.go:10: AK
PASS
ok command-line-arguments 0.003s

TestA 和 TestAK 的测试用例都被执行,原因是-run跟随的测试用例的名称支持正则表达式,使用-run TestA$即可只执行 TestA 测试用例。

3) 标记单元测试结果

当需要终止当前测试用例时,可以使用 FailNow,参考下面的代码。

测试结果标记(具体位置是./src/chapter11/gotest/fail_test.go

  1. func TestFailNow(t *testing.T) {
  2. t.FailNow()
  3. }

还有一种只标记错误不终止测试的方法,代码如下:

  1. func TestFail(t *testing.T) {
  2. fmt.Println("before fail")
  3. t.Fail()
  4. fmt.Println("after fail")
  5. }

测试结果如下:

=== RUN   TestFail
before fail
after fail
--- FAIL: TestFail (0.00s)
FAIL
exit status 1
FAIL command-line-arguments 0.002s

从日志中看出,第 5 行调用 Fail() 后测试结果标记为失败,但是第 7 行依然被程序执行了。

4) 单元测试日志

每个测试用例可能并发执行,使用 testing.T 提供的日志输出可以保证日志跟随这个测试上下文一起打印输出。testing.T 提供了几种日志输出方法,详见下表所示。

单元测试框架提供的日志方法
方  法 备  注
Log 打印日志,同时结束测试
Logf 格式化打印日志,同时结束测试
Error 打印错误日志,同时结束测试
Errorf 格式化打印错误日志,同时结束测试
Fatal 打印致命日志,同时结束测试
Fatalf 格式化打印致命日志,同时结束测试

开发者可以根据实际需要选择合适的日志。

基准测试——获得代码内存占用和运行效率的性能数据

基准测试可以测试一段程序的运行性能及耗费 CPU 的程度。Go 语言中提供了基准测试框架,使用方法类似于单元测试,使用者无须准备高精度的计时器和各种分析工具,基准测试本身即可以打印出非常标准的测试报告。

1) 基础测试基本使用

下面通过一个例子来了解基准测试的基本使用方法。

基准测试(具体位置是./src/chapter11/gotest/benchmark_test.go

  1. package code11_3
  2. import "testing"
  3. func Benchmark_Add(b *testing.B) {
  4. var n int
  5. for i := 0; i < b.N; i++ {
  6. n++
  7. }
  8. }

这段代码使用基准测试框架测试加法性能。第 7 行中的 b.N 由基准测试框架提供。测试代码需要保证函数可重入性及无状态,也就是说,测试代码不使用全局变量等带有记忆性质的数据结构。避免多次运行同一段代码时的环境不一致,不能假设 N 值范围。

使用如下命令行开启基准测试:

$ go test -v -bench=. benchmark_test.go
goos: linux
goarch: amd64
Benchmark_Add-4 20000000 0.33 ns/op
PASS
ok command-line-arguments 0.700s

代码说明如下:

  • 第 1 行的-bench=.表示运行 benchmark_test.go 文件里的所有基准测试,和单元测试中的-run类似。
  • 第 4 行中显示基准测试名称,2000000000 表示测试的次数,也就是 testing.B 结构中提供给程序使用的 N。“0.33 ns/op”表示每一个操作耗费多少时间(纳秒)。

注意:Windows 下使用 go test 命令行时,-bench=.应写为-bench="."

2) 基准测试原理

基准测试框架对一个测试用例的默认测试时间是 1 秒。开始测试时,当以 Benchmark 开头的基准测试用例函数返回时还不到 1 秒,那么 testing.B 中的 N 值将按 1、2、5、10、20、50……递增,同时以递增后的值重新调用基准测试用例函数。

3) 自定义测试时间

通过-benchtime参数可以自定义测试时间,例如:

$ go test -v -bench=. -benchtime=5s benchmark_test.go
goos: linux
goarch: amd64
Benchmark_Add-4 10000000000 0.33 ns/op
PASS
ok command-line-arguments 3.380s

4) 测试内存

基准测试可以对一段代码可能存在的内存分配进行统计,下面是一段使用字符串格式化的函数,内部会进行一些分配操作。

  1. func Benchmark_Alloc(b *testing.B) {
  2. for i := 0; i < b.N; i++ {
  3. fmt.Sprintf("%d", i)
  4. }
  5. }

在命令行中添加-benchmem参数以显示内存分配情况,参见下面的指令:

$ go test -v -bench=Alloc -benchmem benchmark_test.go
goos: linux
goarch: amd64
Benchmark_Alloc-4 20000000 109 ns/op 16 B/op 2 allocs/op
PASS
ok command-line-arguments 2.311s

代码说明如下:

  • 第 1 行的代码中-bench后添加了 Alloc,指定只测试 Benchmark_Alloc() 函数。
  • 第 4 行代码的“16 B/op”表示每一次调用需要分配 16 个字节,“2 allocs/op”表示每一次调用有两次分配。

开发者根据这些信息可以迅速找到可能的分配点,进行优化和调整。

5) 控制计时器

有些测试需要一定的启动和初始化时间,如果从 Benchmark() 函数开始计时会很大程度上影响测试结果的精准性。testing.B 提供了一系列的方法可以方便地控制计时器,从而让计时器只在需要的区间进行测试。我们通过下面的代码来了解计时器的控制。

基准测试中的计时器控制(具体位置是./src/chapter11/gotest/benchmark_test.go):

  1. func Benchmark_Add_TimerControl(b *testing.B) {
  2. // 重置计时器
  3. b.ResetTimer()
  4. // 停止计时器
  5. b.StopTimer()
  6. // 开始计时器
  7. b.StartTimer()
  8. var n int
  9. for i := 0; i < b.N; i++ {
  10. n++
  11. }
  12. }

从 Benchmark() 函数开始,Timer 就开始计数。StopTimer() 可以停止这个计数过程,做一些耗时的操作,通过 StartTimer() 重新开始计时。ResetTimer() 可以重置计数器的数据。

计数器内部不仅包含耗时数据,还包括内存分配的数据。

golang学习笔记 --go test的更多相关文章

  1. golang学习笔记20 一道考察对并发多协程操作一个共享变量的面试题

    golang学习笔记20 一道考察对并发多协程操作一个共享变量的面试题 下面这个程序运行的能num结果是什么? package main import ( "fmt" " ...

  2. golang学习笔记19 用Golang实现以太坊代币转账

    golang学习笔记19 用Golang实现以太坊代币转账 在以太坊区块链中,我们称代币为Token,是以太坊区块链中每个人都可以任意发行的数字资产.并且它必须是遵循erc20标准的,至于erc20标 ...

  3. golang学习笔记18 用go语言编写移动端sdk和app开发gomobile

    golang学习笔记18 用go语言编写移动端sdk和app开发gomobile gomobile的使用-用go语言编写移动端sdk和app开发https://blog.csdn.net/u01249 ...

  4. golang学习笔记17 爬虫技术路线图,python,java,nodejs,go语言,scrapy主流框架介绍

    golang学习笔记17 爬虫技术路线图,python,java,nodejs,go语言,scrapy主流框架介绍 go语言爬虫框架:gocolly/colly,goquery,colly,chrom ...

  5. golang学习笔记16 beego orm 数据库操作

    golang学习笔记16 beego orm 数据库操作 beego ORM 是一个强大的 Go 语言 ORM 框架.她的灵感主要来自 Django ORM 和 SQLAlchemy. 目前该框架仍处 ...

  6. golang学习笔记14 golang substring 截取字符串

    golang学习笔记14 golang substring 截取字符串golang 没有java那样的substring函数,但支持直接根据 index 截取字符串mystr := "hel ...

  7. golang学习笔记13 Golang 类型转换整理 go语言string、int、int64、float64、complex 互相转换

    golang学习笔记13 Golang 类型转换整理 go语言string.int.int64.float64.complex 互相转换 #string到intint,err:=strconv.Ato ...

  8. golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题

    golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题 今天测试了重新建一个项目生成新的表,然后复制到旧的项目 ...

  9. golang学习笔记11 golang要用jetbrain的golang这个IDE工具开发才好

    golang学习笔记11   golang要用jetbrain的golang这个IDE工具开发才好  jetbrain家的全套ide都很好用,一定要dark背景风格才装B   从File-->s ...

  10. golang学习笔记10 beego api 用jwt验证auth2 token 获取解码信息

    golang学习笔记10 beego api 用jwt验证auth2 token 获取解码信息 Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放 ...

随机推荐

  1. Mybatis插入实体类字段为关键字解决方案

    1. Mybatis插入实体类字段为关键字解决方案 1.1. 前言 可能你插入字段为关键字时报如下错误,且字段名不适合改变 You have an error in your SQL syntax; ...

  2. python部分笔记

    创建类和对象 图片转在自:https://www.cnblogs.com/aylin/p/5547558.html 图片转在自:https://www.cnblogs.com/aylin/p/5547 ...

  3. 【Android】天气应用

    模仿华为的"天气"应用写的一个小Demo.部分功能.动画效果没有实现,也没有过多考虑性能.Bug等其它方面的因素.写这个Demo的初衷是想熟悉下目前网上常用的一些框架. Demo采 ...

  4. 【设计模式】Factory

    前言 这篇博客将包括两种设计模式,一种是工厂模式,一种是抽象工厂模式.不管是哪一种设计模式,本质上在解决的问题是对象创建的问题.工厂,可以是简简单单的一个函数,也可以是一个有具体实现的类,也可以是一个 ...

  5. 将流数据输出到Mysql中

    outputMysqlApp.scala import java.sql.DriverManager import org.apache.spark.SparkConf import org.apac ...

  6. c# 第12节 分支语句if、switch、三位运算符

    本节内容: 1:流程控制语句分类 2:分支语句之if的使用 3:分支语句之switch的使用 4:分支语句之三位运算符 5:分支语句比较 1:流程控5:制语句分类 2:分支语句之if 3:分支语句之s ...

  7. 201871010123-吴丽丽 《面向对象程序设计(Java)》第十四周学习总结

    项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...

  8. 201871010104-陈园园 《面向对象程序设计(java)》第十二周学习总结

    201871010104-陈园园 <面向对象程序设计(java)>第十二周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ ...

  9. LG5367 「模板」康托展开 康托展开

    问题描述 LG5367 题解 康托展开公式: \[ans=1+(\sum_{i=1}^{n}{a_i})\times(n-i)!\] 用树状数组维护一下\(\sum\)里面的东西,前缀积维护后面的东西 ...

  10. 【转】机器学习实战之K-Means算法

    一,引言 先说个K-means算法很高大上的用处,来开始新的算法学习.我们都知道每一届的美国总统大选,那叫一个竞争激烈.可以说,谁拿到了各个州尽可能多的选票,谁选举获胜的几率就会非常大.有人会说,这跟 ...