Golang单元测试与覆盖率的实例讲解
亲爱的编程学习爱好者,如果你点开了这篇文章,说明你对《Golang单元测试与覆盖率的实例讲解》很感兴趣。本篇文章就来给大家详细解析一下,主要介绍一下测试、单元、覆盖率,希望所有认真读完的童鞋们,都有实质性的提高。
1 概述
C/C++和Java(以及大多数的主流编程语言)都有自己成熟的单元测试框架,前者如Check,后者如JUnit,但这些编程框架本质上仍是第三方产品,为了执行单元测试,我们不得不从头开始搭建测试工程,并且需要依赖于第三方工具才能生成单元测试的覆盖率。
相比之下,Go语言官方则提供了语言级的单元测试支持,即testing包,而且仅通过go工具本身就可以方便地生成覆盖率数据,也就是说,单元测试是Go语言的自带属性,除了好好设计自己的单元测试用例外,开发者不需要操心工程搭建的任何细节。没错,Golang就是这么任性。
2 单元测试
下面我们以《The Go Programming Language》6.5节的比特容器为例,介绍如何通过testing包和go工具集进行单元测试。
2.1 工程目录
不是说好的,Go语言单元测试不需要搭建测试工程么?其实,Golang的测试工程只有一句话:对file.go新建file_test.go文件,并在其中编写测试用例。所以,我们所谓的工程目录其实就是:
$ go env | grep GOPATH
GOPATH="/home/pirlo/go"
$ tree /home/pirlo/go/src/github.com/pirlo-san/let-us-go
/home/pirlo/go/src/github.com/pirlo-san/let-us-go
├── bitvector
│ ├── bitvector.go
│ └── bitvector_test.go
├── LICENSE
└── README.md
/home/pirlo/go是我的GOPATH,其中的github.com/pirlo-san/let-us-go是一个git工程,bitvector则是这个工程下的一个子模块,即比特容器模块,bitvector.go是模块的实现文件,bitvector_test.go则是用于测试比特容器的文件。
2.2 比特容器的实现
Golang没有容器类型,多数容器都是通过map[type]bool实现的,但是通过map实现在某些场景下比较浪费内存,比如容器元素都是一些很小的非负整数的场景:0~31,其实,我们只需要一个uint32类型4个字节就可以了,但是如果采用map[uint32]bool实现,则对每个元素都需要一个uint32的key和bool类型的value。在C/C++语言内,可以很容易地通过位域的方式达到节省内存的目的,那么Golang可不可以采用类似的方式实现呢?当然可以喽。
2.2.1 定义
type IntSet struct {
words []uint
}
const (
wordBitCount = (32 > 63))
)
IntSet是我们定义的比特容器类型,是一个结构体,其中唯一的成员是一个uint类型的切片,想象切片的元素被有序排列成一个“比特”数组,如果容器内存在元素N,则这个数组的第N个元素的值就为1,否则就是0.
wordBitCount用于计算uint类型占用的比特数,这个数字在不同的操作系统或CPU上是不同的。
2.2.2 向容器内添加一个元素
// add x into set s
func (s *IntSet) Add(x int) {
word, index := wordIndex(x)
for word >= len(s.words) {
s.words = append(s.words, 0)
}
s.words[word] |= (1
先获取这个元素在第几个“word”,以及在这个word内的第几个比特,如果words切片长度不够,则一直添加到可以包含待插入的元素为止,最后将对应元素位置的“比特位”设置为1.
2.2.3 判断某元素是否在容器内
// check wether x is in set s
func (s *IntSet) Has(x int) bool {
word, index := wordIndex(x)
if word >= len(s.words) {
return false
}
return (s.words[word] & (1
《The Go Programming Language》内还实现了其它接口,包括String,UnionWith等,完整代码见文末链接。
2.3 单元测试用例
好了,为了测试这个比特容器模块,我们只需要在package目录内定义相应的test文件,并编写用例即可。本例即为bitvector_test.go:
package bitvector
import (
"testing"
)
func TestAdd(t *testing.T) {
var s IntSet
s.Add(1)
s.Add(2)
s.Add(3)
s.Add(4)
if s.Has(1) == false || s.Has(2) == false || s.Has(3) == false || s.Has(4) == false {
t.Error("Failed")
}
if s.Has(0) == true || s.Has(5) == true || s.Has(100) == true {
t.Error("Failed")
}
}
包声明:测试文件也归属于bitvector包,这样测试文件就可以随意访问这个包已导出和未导出的类型、函数、方法等;你可以定义成不同的包,比如package bitvector_test,这样,bitvector包对bitvector_test包来说就是一个外部库,test包只能访问其中已导出的类型、函数、方法等,这个叫做外部测试;
导入testing包:testing包拥有执行Golang单元测试所需要的一切;
编写测试函数:所有测试函数都以Test开头,入参是testing.T类型的指针,在函数内调用被测函数,并对不符合预期的结果调用类似Error、Fatal的函数,其中前者在被调用后会打印出错信息,并继续执行后续用例,而后者则在打印信息后立即终止测试,一般仅在测试出现严重问题,无法继续进行后续用例测试时才需要调用类似Fatal的接口。
2.4 执行单元测试
Golang执行单元测试的命令是go test,如果你在待测package所在的目录,则直接执行go test即可:
$ pwd
/home/pirlo/go/src/github.com/pirlo-san/let-us-go/bitvector
$ go test
PASS
ok github.com/pirlo-san/let-us-go/bitvector 0.004s
不带任何参数的情况下,test仅输出最终的测试结果,如果要看到测试过程,可以指定-v参数:
$ go test -v
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok github.com/pirlo-san/let-us-go/bitvector 0.004s
每个用例的执行成功与否,以及执行用时都会显示出来。
如果不在当前目录,则需要指定待测模块路径:
$ pwd
/home/pirlo/go
$ go test -v github.com/pirlo-san/let-us-go/bitvector/
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok github.com/pirlo-san/let-us-go/bitvector 0.004s
甚至,你还可以执行所有模块的测试,方式是以三个点替代具体的模块路径:
$ go test -v ...
3 覆盖率生成
Golang单元测试覆盖率的生成也简单到令人发指。两步:
执行go test时指定-coverprofile参数收集覆盖率数据;
执行go tool cover生成文本、html等可视化格式的覆盖率报告。
3.1 收集覆盖率数据
$ go test -v -coverprofile=cover.out github.com/pirlo-san/let-us-go/bitvector/
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
PASS
coverage: 36.0% of statements
ok github.com/pirlo-san/let-us-go/bitvector 0.009s
$ ll cover.out
-rw-rw-r-- 1 pirlo pirlo 1330 Jan 12 23:11 cover.out
3.2 生成html格式的覆盖率报告
$ go tool cover -html=cover.out -o coverage.html
$ ll coverage.html
-rw-rw-r-- 1 pirlo pirlo 4504 Jan 12 23:15 coverage.html
生成的覆盖率报告效果如下:

其中第一行左侧的下拉列表列举了所有文件的覆盖率百分比,正文则以蓝绿色字体标识已覆盖的代码行(本例的Add和Has都已经被测试过了),以红色字体标识未被覆盖的代码行(UnionWith还没有对应的测试用例),灰色字体则是类似类型定义、函数声明等不需要被跟踪的代码行。
4 小结
Golang的单元测试和覆盖率报告生成,过程非常简单迅捷,而且不需要借助任何第三方工具或库,除了本文所述的基本测试场景外,Golang还支持Benchmark测试、内部函数/方法打桩等,有空再聊。
本文完整代码在:这里
补充知识:GoLang Test 显示输出
默认运行 go test 不会输出 testing.T.Log() 的内容。
要显示这些内容,需要加上开关 -v
go test -v -timeout 30s xxx/xxx/package -run ^TestXXXFunction$
在 Visual Studio Code IDE 环境中,可以设置 Workspace Settings。打开 .vscode/settings.json,添加:
"go.testFlags": ["-v"],
这样,在 IDE 编辑器中,点击函数上方的 run test,自动运行 go test,会被加上 -v 标志,在 OUTPUT 窗口就可以看到 t.Logf("xxx%s","xxx") 的输出内容了。
未加设置前:

添加设置后:

以上这篇Golang单元测试与覆盖率的实例讲解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持golang学习网。
终于介绍完啦!小伙伴们,这篇关于《Golang单元测试与覆盖率的实例讲解》的介绍应该让你收获多多了吧!欢迎大家收藏或分享给更多需要学习的朋友吧~golang学习网公众号也会发布Golang相关知识,快来关注吧!
goland 搭建 gin 框架的步骤详解
- 上一篇
- goland 搭建 gin 框架的步骤详解
- 下一篇
- logrus hook输出日志到本地磁盘的操作
-
- 喜悦的帽子
- 受益颇多,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢楼主分享文章内容!
- 2023-03-02 11:36:40
-
- 自然的灯泡
- 好细啊,mark,感谢作者大大的这篇技术文章,我会继续支持!
- 2023-02-09 09:03:14
-
- 开心的枫叶
- 这篇技术贴真是及时雨啊,很详细,很好,码起来,关注老哥了!希望老哥能多写Golang相关的文章。
- 2023-02-07 12:38:26
-
- 踏实的牛排
- 太详细了,码住,感谢大佬的这篇文章内容,我会继续支持!
- 2023-01-22 02:15:38
-
- 务实的鸭子
- 很有用,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢作者分享技术文章!
- 2023-01-16 18:20:33
-
- 英俊的冷风
- 这篇技术文章真是及时雨啊,细节满满,很棒,收藏了,关注up主了!希望up主能多写Golang相关的文章。
- 2023-01-05 23:37:18
-
- Golang · Go教程 | 27分钟前 | 反射 · go · 结构体标签 · Go 反射 json标签 reflect.StructTag
- Go 反射读取 json 标签为空时怎么区分未声明和空值
- 360浏览 收藏
-
- Golang · Go教程 | 39分钟前 | 反射 · Go教程 · 结构体标签 · 字段元数据 · Go reflect.StructTag StructTag.Lookup StructTag.Get
- Go reflect.StructTag 怎么读取自定义字段标签
- 291浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go iter.Seq2 返回键值时怎么避免复制大对象
- 441浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go iter.Seq 怎么把分页查询包装成惰性遍历
- 203浏览 收藏
-
- Golang · Go教程 | 1小时前 | 排序 · go · Slices · Maps · Go maps.Keys slices.Sorted slices.Sort
- Go maps.Keys 收集键后怎么得到稳定排序结果
- 158浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go maps.Copy 合并配置时怎么明确覆盖方向
- 391浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go maps.Clone 复制嵌套 map 时哪些数据仍然共享
- 501浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- Go slices.Insert 批量插入时怎么判断容量是否复用
- 109浏览 收藏
-
- Golang · Go教程 | 2小时前 | go · Slices · 内存管理 · Go 切片 slices.Delete 内存引用
- Go slices.Delete 删除元素后怎么避免旧数组残留引用
- 200浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 19次使用
-
- SuperCLUE
- SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
- 174次使用
-
- C-Eval
- 深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
- 110次使用
-
- AI Prompt Library
- 探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
- 37次使用
-
- Generrated
- Generrated汇集9300+张DALL·E生成图像及对应提示词,支持查看完整图集、对比DALL·E 2与3版本差异,是AI绘图新手学习Prompt设计与获取创作灵感的实用工具。
- 17次使用
-
- 详解Go 语言如何通过测试保证质量
- 2022-12-29 290浏览
-
- Golang 单元测试和基准测试实例详解
- 2022-12-23 275浏览
-
- GoLang基础学习之go test测试
- 2023-01-01 265浏览
-
- Go语言单元测试超详细解析
- 2023-01-07 185浏览
-
- golang分层测试之http接口测试入门教程
- 2023-02-16 444浏览
