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
posted @ 2023-06-01 15:18  Jrri  阅读(7)  评论(0编辑  收藏  举报