Go 单元测试执行案例
目录
单元测试的概念
什么是单元测试
为什么要写单元测试
编写测试用例的原则
Go约定的测试规则
单元测试的实践过程
编写一个简单的测试用例
真实案例
被测试的函数
简单的测试
多维度测试案例
GoFrame 中依赖框架配置文件的单元测试
加载测试用例的配置文件
引入环境,并编写mysql、redis的测试用例
第三方测试框架 (后续再补充~)
参考文档
单元测试的概念
Gopher一定要养成写单元测试的习惯,这样才能保证我们交付代码的质量,同时提升个人开发水平!
什么是单元测试
1. 最小的可测试单位,比如函数、对象的某个接口
2. 是软件开发过程中对最小单位进行正确性验证的测试工作
为什么要写单元测试
1. 保证变更、重构的正确性,特别是在多人协作的项目中
2. 简化调试过程:可以快速定位到问题代码
3. 单元测试是最好的开发文档:单元测试给出具体函数的使用方法及边界值,是最好的示例代码
编写测试用例的原则
1. 单一原则:一个测试用例只负责一个应用场景
2. 原子性:结果只有两种情况:Pass/Fail
3. 优先级:核心的组件、逻辑要优先测试
4. 公共使用库: utils工具类库要重点覆盖
Go约定的测试规则
1. 单元测试的文件需要和被测试的文件在统一目录下,测试用例名称必须是xxx_test.go的格式
测试文件中包含三种类型的函数,单元测试函数、基准测试函数和示例函数。
| 类型 | 格式 | 作用 |
| 单元测试函数 | 函数名前缀为Test | 测试程序的逻辑行为是否达到预期 |
| 基准函数 | 函数名前缀为Benchmark | 测试函数的性能 |
| 示例函数 | 函数名前缀为Example | 为doc文档提供示例 |
单元测试的实践过程
编写一个简单的测试用例
package serviceimport "testing"func add(a, b int) int {return a + b
}func TestAdd(t *testing.T) {a := 10b := 20wanted := 30actual := add(a, b)if wanted != actual {t.Errorf("[add函数的入参:%d %d] [期望值:%d] [实际结果:%d]", a, b, wanted, actual)}
}
执行当前单元测试的命令: go test -v .\user_test.go
PS C:\Program Files\JetBrains\GoRepository\gf-demo-user\internal\service> go test -v .\user_test.go
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok command-line-arguments 0.075s
执行当前目录下所有的测试用例(跳过某个测试函数),并导出到制定的输出文件:go test -v ./... --short=true -coverprofile cover.out
PS C:\Program Files\JetBrains\GoRepository\gf-demo-user\internal\service> go test -v ./... --short=true -coverprofile cover.out
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
coverage: 0.0% of statements
ok github.com/gogf/gf-demo-user/v2/internal/service 0.121s coverage: 0.0% of statements
查看输出覆盖率的html文档:go tool cover --func=cover.out
PS C:\Program Files\JetBrains\GoRepository\gf-demo-user\internal\service> go tool cover --func=cover.out
github.com/gogf/gf-demo-user/v2/internal/service/biz_ctx.go:22: BizCtx 0.0%
github.com/gogf/gf-demo-user/v2/internal/service/biz_ctx.go:29: RegisterBizCtx 0.0%
github.com/gogf/gf-demo-user/v2/internal/service/middleware.go:19: Middleware 0.0%
github.com/gogf/gf-demo-user/v2/internal/service/middleware.go:26: RegisterMiddleware 0.0%
github.com/gogf/gf-demo-user/v2/internal/service/session.go:21: Session 0.0%
github.com/gogf/gf-demo-user/v2/internal/service/session.go:28: RegisterSession 0.0%
github.com/gogf/gf-demo-user/v2/internal/service/user.go:26: User 0.0%
github.com/gogf/gf-demo-user/v2/internal/service/user.go:33: RegisterUser 0.0%
total: (statements) 0.0%
真实案例
被测试的函数
func Split(s, sub string) (result []string) {if len(strings.TrimSpace(s)) == 0 {return}i := strings.Index(s, sub)for i > -1 {if i == 0 {s = s[len(sub):]} else {result = append(result, s[:i])s = s[i+len(sub):]}i = strings.Index(s, sub)}result = append(result, s)return
}
简单的测试
get := Split("a:b:c", ":")wanted := []string{"a", "b", "c"}if !reflect.DeepEqual(wanted, get) {t.Errorf("expected:%v,get:%v", wanted, get)}
多维度测试案例
func TestMultiSplit(t *testing.T) {type test struct {name stringinput stringsub stringwanted []string}tests := []test{{name: "正常的测试用例", input: "a:b:c", sub: ":", wanted: []string{"a", "b", "c"}},{name: "字符串头位置匹配测试", input: ":a:b:c", sub: ":", wanted: []string{"a", "b", "c"}},{name: "无匹配项的测试", input: "abc", sub: ":", wanted: []string{"abc"}},{name: "英文串的测试", input: "abcd", sub: "bc", wanted: []string{"a", "d"}},{name: "中文串的测试", input: "云原生", sub: "云", wanted: []string{"原生"}},}for _, d := range tests {get := Split(d.input, d.sub)if !reflect.DeepEqual(d.wanted, get) {t.Errorf("[testName:%v][expected:%v] [get:%v]", d.name, d.wanted, get)}}
}
GoFrame 中依赖框架配置文件的单元测试
加载测试用例的配置文件
root.go
package testingutilimport ("path""runtime"
)func RootDir() string {_, d, _, _ := runtime.Caller(0)return path.Dir(path.Dir(d))
}
root_test.go
package testingutilimport "testing"func TestRootDir(t *testing.T) {rootDir := RootDir()t.Log(rootDir)
}func BenchmarkRootDir(b *testing.B) {for i := 0; i < b.N; i++ {RootDir()}
}
初始化加载GoFrame配置环境
config.go
package testingutilimport ("github.com/gogf/gf/v2/os/genv""path"
)func init() {InitConfig()
}func InitConfig() {configDir := path.Join(RootDir(), "manifest/config")genv.Set("GF_GCFG_PATH", configDir)
}
引入环境,并编写mysql、redis的测试用例
func TestMysql(t *testing.T) {testingutil.InitConfig()ctx := gctx.New()countSql := "select collection_id,count(1) as count from transfer_logs" + " where 1 = 1" + " " + " group by collection_id"var countList []struct {CollectionId int64 `json:"collection_id"`Count int `json:"count"`}// 获取内容all, err := g.DB().Ctx(ctx).GetAll(ctx, countSql)if err != nil {t.Fatal(err)}all.Structs(&countList)fmt.Printf("%+v", countList)
}func TestRedis(t *testing.T) {testingutil.InitConfig()ctx := gctx.New()cache := gcache.NewAdapterRedis(g.Redis())err := cache.Set(ctx, 1, 1, time.Second*10)if err != nil {t.Fatal(err)}
}
第三方测试框架 (后续再补充~)
1. GoConvey测试框架
2. testify
参考文档
GO单元测试
go test命令(Go语言测试命令)完全攻略
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
