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 service

import "testing"

func add(a, b int) int {
	return a + b
}

func TestAdd(t *testing.T) {
	a := 10
	b := 20
	wanted := 30
	actual := 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   string
		input  string
		sub    string
		wanted []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 testingutil

import (
	"path"
	"runtime"
)

func RootDir() string {
	_, d, _, _ := runtime.Caller(0)
	return path.Dir(path.Dir(d))
}

root_test.go

package testingutil

import "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 testingutil

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

你可能感兴趣的:(Go,单元测试,junit)