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语言测试命令)完全攻略


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部