Go 单元测试
单元测试
在执行单元测试的过程中, 如果被测试的函数中, 引用了较为复杂的函数.
要确保该引用包中在引用时已被初始化, 避免空出现内存/指针错误等问题~
单元测试编写
测试示例
逻辑代码: string_handler.go
func stringBuilder(ss ...string) string {
var s = strings.Builder{}
for _, v := range ss {
s.WriteString(v)
}
return s.String()
}
测试代码: string_handler_test.go
func Test_stringBuilder(t *testing.T) {
type args struct {
ss []string
}
tests := []struct {
name string
args args
want string
}{
// TODO: Add test cases.
{
name: "正向测试",
args: args{[]string{"test", "-", "test"}},
want: "test-test",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := stringBuilder(tt.args.ss...); got != tt.want {
t.Errorf("stringBuilder() = %v, want %v", got, tt.want)
}
})
}
}
测试文件命名
在原文件名后添加 _test
// 原文件名: run_func.go
// 测试文件: run_func_test.go
main.go
-> main_test.go
svn_user.go
-> svn_user_test.go
git_repo.go
-> git_repo_test.go
语法与细节
测试函数使用Test_
开头, 传入对象t *testing.T
.
// 原函数名: stringBuilder
// 测试函数: Test_stringBuilder
测试代码分为三部分:
- 数据结构: 声明用于测试的数据结构, 用于测试的测试内容集.
- 数据内容: 声明用于测试的数据内容(名称, 变量数据, 期待数据)
- 运行测试逻辑.
// 遍历测试数据集, 命名为tt
for _, tt := range tests {
// 使用testing.T.run , 运行测试(测试名, 测试用匿名函数)
t.Run(tt.name, func(t *testing.T) {
// 判断输出值与期待的目标值是否相同
if got := stringBuilder(tt.args.ss...); got != tt.want {
// 不同时, 使用testing.T中ErrorF输出前后内容.
t.Errorf("stringBuilder() = %v, want %v", got, tt.want)
}
})
}
IDE自动生成模板
在使用Goland的时候, 可以一键生成单元测试的代码文件.
通常针对文件生成:
alt
+ insert
, 唤出生成小窗. tests for file
在当前目录生成该文件的测试文件.
即可自动生成单元测试框架的代码文件, 在 // TODO: Add test cases.
中填写对应的测试数据集即可.
运行单元测试
-
IDE:
在IDE中, 可以直接点选函数对应的单元测试, 甚至是对应的某一个测试数据运行测试过程.
可根据需求, 选择对应的测试用例进行对应的测试. -
CMD:
在命令行环境下, 可使用对应命令调用测试过程:
# 调用测试过程(自动的执行当前目录下`_test.go`后缀的文件)
go test
# 调用测试过程, 并显示具体的测试用例详情
go test -v
# 调用测试过程, 并显示覆盖度
go test -cover
# 防止编译器内联优化导致单测出现问题, 使用mock模拟时, 需要开启该功能.
go test -gcflags=all=-l
生成单元测试报告
# 生成覆盖率数据, 以生成的覆盖率数据制成html显示
go test -coverprofile=coverage.out
go tool cover -html=coverage.out
性能测试编写
性能测试与单元测试都书写在同一个_test.go文件内.
但通常情况下, go test命令不会主动运行性能测试代码.
测试示例
逻辑代码: string_handler.go
func stringBuilder(ss ...string) string {
var s = strings.Builder{}
for _, v := range ss {
s.WriteString(v)
}
return s.String()
}
测试代码: string_handler_test.go
func BenchmarkstringBuilder(t *testing.T) {
type args struct {
ss []string
}
tests := []struct {
name string
args args
want string
}{
// TODO: Add test cases.
{
name: "正向测试",
args: args{[]string{"test", "-", "test"}},
want: "test-test",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := stringBuilder(tt.args.ss...); got != tt.want {
t.Errorf("stringBuilder() = %v, want %v", got, tt.want)
}
})
}
}
语法与细节
测试函数使用Benchmark_
开头, 传入对象b *testing.B
.
// 原函数名: stringBuilder
// 测试函数: Benchmark_stringBuilder
性能测试代码分为两部分:
- 数据结构: 声明用于测试的数据结构, 用于测试的测试内容集.
- 运行测试逻辑.
// 遍历测试数据集, 命名为tt
for _, tt := range tests {
// 使用testing.T.run , 运行测试(测试名, 测试用匿名函数)
t.Run(tt.name, func(t *testing.T) {
// 判断输出值与期待的目标值是否相同
if got := stringBuilder(tt.args.ss...); got != tt.want {
// 不同时, 使用testing.T中ErrorF输出前后内容.
t.Errorf("stringBuilder() = %v, want %v", got, tt.want)
}
})
}
运行性能测试
- CMD:
在命令行环境下, 可使用对应命令调用测试过程:
# 调用性能测试
go test -bench=.
# 调用性能测试, 指定次数: 10次
go test -bench=. -count=10
# 调用性能测试, 指定时间
go test -bench=. -benchtime=1s