Golang单元测试中的技巧分享
作者:LinkinStar 发布时间:2024-04-23 09:47:32
打桩测试
当我们在编写单元测试的时候,有时我们非常想 mock 掉其中一个方法,但是这个方法又没有接口去定义和实现(无法用 github.com/golang/mock
来实现),这时就可以尝试看看我们的打桩黑科技。
代码
这里我们使用 github.com/agiledragon/gomonkey 来实现。
实际中,经常在代码中会遇到一些随机值的情况,比如验证码。为了方便测试,我们会想要 mock 掉随机值方法,让每次产生的值固定方便后续的测试。
package main
import (
"fmt"
"testing"
"github.com/agiledragon/gomonkey/v2"
"go-demo/m/unit-test/other/rand"
)
func init() {
gomonkey.ApplyFunc(rand.Number, func() int { return 666 })
}
func TestRand(t *testing.T) {
fmt.Println(rand.Number())
}
其中 rand.Number()
是我们在另一个包中实现的方法。我们使用 gomonkey.ApplyFunc
相当于直接替换了原有方法的实现,强制返回了 mock 的数据 666。
注意事项
使用
gomonkey
时,注意一定要使用-gcflags=all=-l
来禁止内联优化,否则容易导致打桩不生效。如:go test -gcflags=all=-l -v
在 Mac 的 M1 下打桩不生效,可以使用环境变量
GOARCH=amd64
来进行测试,只不过这样就无法进行断点调试。https://github.com/agiledragon/gomonkey/issues/77毕竟是黑科技,实际使用环境对于测试还是有影响的。
压测
这里的压测通常不是对接口的压测,而是对于某些方法的压测。Golang 提供 非常好用的 b *testing.B
来专门进行压测。
代码
非常容易上手,让我们直接来看代码
var numbers = []int{
100,
1000,
77777,
666666,
}
func BenchmarkPrimeNumbers(b *testing.B) {
for _, v := range numbers {
b.Run(fmt.Sprintf("calc_num_%d", v), func(b *testing.B) {
for i := 0; i < b.N; i++ {
primeNumbers(v)
}
})
}
}
使用
使用 -bench=.
即可
➜ go test -bench=. b_test.go
goos: darwin
goarch: arm64
BenchmarkPrimeNumbers/calc_num_100-8 3391329 348.9 ns/op
BenchmarkPrimeNumbers/calc_num_1000-8 293733 3979 ns/op
BenchmarkPrimeNumbers/calc_num_77777-8 730 1619089 ns/op
BenchmarkPrimeNumbers/calc_num_666666-8 42 28509805 ns/op
PASS
ok command-line-arguments 5.846s
可以看到次数和耗时,这样的数据经常会被贴在开源项目README的性能比较板块中。
测试覆盖率
单元测试当然少不了覆盖率咯,看着高高的覆盖率才有成就感对不对?
我们可以使用 -cover
参数来统计单元测试的代码覆盖率
➜ go test -cover .
ok go-demo/m/unit-test/service 0.879s coverage: 70.0% of statements
还可以生成 profile 然后通过可视化网页查看分析
➜ go test ./... -coverprofile=cover.out
ok go-demo/m/unit-test/service 0.108s coverage: 70.0% of statements
➜ go tool cover -html=cover.out
当然,如果你和我一样使用 goland 进行开发,可以直接在界面上点击单元测试,就可以快速进行覆盖率测试,并且展示也很方便,很快能看到有哪些代码没有被测试覆盖。
表格驱动测试
TDT 的提出也算是给单元测试提供了一条不错的思路。主要的形式就是将测试的数据做成类似表格,然后测试的时候遍历所有的数据来进行测试,测试代码不动,只需要增加测试数据就可以了。go 很多官方库用了这样的写法来做测试。
package main
import (
"testing"
)
func TestAdd(t *testing.T) {
tests := []struct {
a, b, expected int
}{
{1, 2, 3},
{0, 0, 0},
{-1, 1, 0},
{-1, -1, -2},
}
for _, test := range tests {
result := Add(test.a, test.b)
if result != test.expected {
t.Errorf("Add(%d, %d) = %d; expected %d", test.a, test.b, result, test.expected)
}
}
}
func Add(a, b int) int {
return a + b
}
我们可以通过 github.com/cweill/gotests 来帮助我们快速生成想要的表格驱动测试代码
来源:https://segmentfault.com/a/1190000043526318
猜你喜欢
- 在调试爬虫的时候,新手都会遇到关于ip的错误,好好的程序突然报错了,怎么解决,关于ip访问的错误其实很好解决,但是怎么知道解决好了呢?怎么确
- 树树(Tree)是n(n≥0)个节点的有限集。在任意一棵树中:(1)有且仅有一个特定的称为根(Root)的节点;(2)当n>1
- 需求:小程序端拍照调用python训练好的图片分类模型。实现图片分类识别的功能。微信小程序端:重点在chooseImage函数中,根据图片路
- 变量不是盒子在示例所示的交互式控制台中,无法使用“变量是盒子”做解释。图说明了在 Python 中为什么不能使用盒子比喻,而便利贴则指出了变
- 最近在折腾验证码识别。最终的脚本的识别率在92%左右,9000张验证码大概能识别出八千三四百张左右。好吧,其实是验证码太简单。下面就是要识别
- //截取字符串长度。支持utf-8和gb2312编码。若为gb2312,先将其转为utf-8,在utf-8的基础上截取然后再转换回来 fun
- mysql中用命令行复制表结构的方法主要有一下几种: 1.只复制表结构到新表CREATE TABLE 新表 SELECT * FR
- 前言Django自带一个用户认证系统,用于处理用户账户、群组、许可和基于cookie的用户会话。Django的认证系统包含了身份验证和权限管
- Date.prototype.format = function(format){ var o = { "M+" : t
- 1.安装完成自动化测试,需要配置三个东西。selenium:pip就可以了chrome:浏览器下载一个谷歌浏览器就行chrome-drive
- 最近对 Range 和 Selection 比较感兴趣。基本非 IE 的浏览器都支持 DOM Level2 中的 Range,而 IE 中仅
- 首先是抓包,使用scapy模块,sniff()函数 在其中参数为本地文件路径时,操作为打开本地文件若参数为BPF过滤规则和回调函数,则进行S
- 本文实例讲述了JS创建对象的写法。分享给大家供大家参考,具体如下:写法1:<script>var database = func
- 如要让数据库进行自动管理,则管理员需要预先定义一些可预测的管理任务以及这些任务发送的条件。当满足这些指定的条件,则数据库会自动运行管理员指定
- 1、过年的时候在手机上下载了2048玩了几天,心血来潮决定用py写一个,刚开始的时候想用QT实现,发现依赖有点大。正好看到graphics.
- js监听浏览器回车事件,可以支持ie6+,火狐,谷歌等浏览器。<html><head><script type
- 个人类型和海外类型的小程序不支持 web-view 标签 也就是说个人申请的小程序,就别想跳转了!!!!1.开发的时候,我们难免
- 踩坑记录:用pandas来做csv的缺失值处理时候发现奇怪BUG,就是excel打开csv文件,明明有的格子没有任何东西,当然,我就想到用p
- 首先数据库里需要有一个自动编号字段(ID)。然后第一次访问的时候,取出所有记录,定制好每页的记录数PageSize,计算出页数,然后根据页数
- 如何提高SQL Server数据库的性能,该从哪里入手呢?笔者认为,该遵循从外到内的顺序,来改善数据库的运行性能。如下图: 第一层