Go中的单元测试和基准测试
文章目录
TDD
Test-Driven Development: 测试驱动开发,是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。
单元测试框架提供的日志方法
方 法 | 备 注 |
---|---|
Log | 打印日志,同时结束测试 |
Logf | 格式化打印日志,同时结束测试,eg: t.Logf("%+v", resp) |
Error | 打印错误日志,同时结束测试 |
Errorf | 格式化打印错误日志,同时结束测试 |
Fatal | 打印致命日志,同时结束测试 |
Fatalf | 格式化打印致命日志,同时结束测试 |
单元测试
Go 为了提高测试的性能,会对包的编译后的测试代码进行缓存。
常用命令
|
|
测试文件中必须导入 “testing” 包,并写一些名字以 TestZzz 打头的全局函数,这里的 Zzz 是被测试函数的字母描述,如 TestFmtInterface,TestPayEmployees 等。比如:
|
|
测试用例案例
|
|
模糊测试
go1.18才可以支持模糊测试。模糊测试是通过指定的规则, 使用模糊参数变异作为输入去测试模糊目标在各种边界条件下的状况的一种测试方式。
常用命令
|
|
示例
模糊测试的适用场景比较有限,如果函数的入参并不是像本例中的那样的简单(字符串),而是各种对象呢?可能它就无能为力了吧。
|
|
基准测试
基准测试主要是通过测试CPU和内存的效率问题,来评估被测试代码的性能,进而找到更好的解决方案。
对于基准测试,go test 会并发执行,默认最大是 8 个并发,可以通过-parallel指定,最终按测试函数在文件中的顺序打印测试结果。
常用命令
|
|
testing 包中有一些类型和函数可以用来做简单的基准测试;测试代码中必须包含以 BenchmarkZzz 打头的函数并接收一个 *testing.B 类型的参数,比如:
|
|
基准测试是测量一个程序在固定工作负载下的性能。在Go语言中,基准测试函数和普通测试函数写法类似,但是以Benchmark为前缀名,并且带有一个*testing.B类型的参数;testing.B参数除了提供和testing.T类似的方法,还有额外一些和性能测量相关的方法。它还提供了一个整数N,用于指定操作执行的循环次数。
基准测试基本原则
在进行基准测试之前,你必须要有一个稳定的环境以得到可重复的输出结果。
- 机器必须是空闲状态 – 不能在共享的硬件上采集数据,当长时间运行基准测试时不能浏览网页等
- 机器是否关闭了节能模式。一般笔记本电脑上会默认开启该模式。
- 避免使用虚拟机和云主机。一般情况下,为了尽可能地提高资源的利用率,虚拟机和云主机 CPU 和内存一般会超分配,超分机器的性能表现会非常地不稳定。
第三方测试包
github.com/stretchr/testify
断言功能强大
|
|
Visualising the Costs and Benefits
在受益于单元测试的好处的同时,也必然增加了代码量以及维护成本(单元测试代码也是要维护的)。下面这张成本/价值象限图很清晰的阐述了在不同性质的系统中单元测试成本和价值之间的关系。
-
依赖很少的简单的代码(左下)
对于外部依赖少,代码又简单的代码。自然其成本和价值都是比较低的。举Go官方库里errors包为例,整个包就两个方法 New()和 Error(),没有任何外部依赖,代码也很简单,所以其单元测试起来也是相当方便。
-
依赖较多但是很简单的代码(右下)
依赖一多,mock和stub就必然增多,单元测试的成本也就随之增加。但代码又如此简单(比如上述errors包的例子),这个时候写单元测试的成本已经大于其价值,还不如不写单元测试。
-
依赖很少的复杂代码 (左上)
像这一类代码,是最有价值写单元测试的。比如一些独立的复杂算法(银行利息计算,保险费率计算,TCP协议解析等),像这一类代码外部依赖很少,但却很容易出错,如果没有单元测试,几乎不能保证代码质量。
-
依赖很多又很复杂(右上)
这种代码显然是单元测试的噩梦。写单元测试吧,代价高昂;不写单元测试吧,风险太高。像这种代码我们尽量在设计上将其分为两部分:1.处理复杂的逻辑部分 2.处理依赖部分 然后1部分进行单元测试
结论:一般编写依赖较少的单元测试代码,收益较高
我们遇到最常见的依赖无非下面几种:
- 网络依赖——函数执行依赖于网络请求,比如第三方http-api,rpc服务,消息队列等等
- 数据库依赖
- I/O依赖(文件)