golang unit test测试
2022-08-10 01:23 youxin 阅读(673) 评论(0) 编辑 收藏 举报Golang单元测试对文件名和方法名,参数都有很严格的要求。
例如:
1、文件名必须以xx_test.go命名
2、方法必须是Test[^a-z]开头
3、方法参数必须 t *testing.T‘
Go语言中的测试依赖go test命令。编写测试代码和编写普通的Go代码过程是类似的,并不需要学习新的语法、规则或工具。
go test命令是一个按照一定约定和组织的测试代码的驱动程序。在包目录内,所有以_test.go为后缀名的源代码文件都是go test测试的一部分,不会被go build编译到最终的可执行文件中。
在_test.go文件中有三种类型的函数,单元测试函数、基准测试函数和示例函数。
go test命令会遍历所有的_test.go文件中符合上述命名规则的函数,然后生成一个临时的main包用于调用相应的测试函数,然后构建并运行、报告测试结果,最后清理测试中生成的临时文件。’‘
’
运行程序时报go: cannot find main module, but found .git/config in
编写单元测试,运行时报下面的错误
haima@haima-PC:/media/haima/34E401CC64DD0E28/site/go/src/haimait/learn/base/cheshi01$ go test
go: cannot find main module, but found .git/config in /media/haima/34E401CC64DD0E28/site/go/src/haimait/learn
to create a module there, run:
cd ../.. && go mod init
出错原因是开启了go mod,但是没有初使化生成go.mod文件
解决办法1:关闭gomod
main函数里测试
package main import "fmt" func main() { res := addUpper(10) fmt.Printf("返回值res:%v", res) } func add(n1 int , n2 int) int { return n1 + n2 } func addUpper(n int) int { res := 0 for i := 0 ; i <= n ; i++ { res += i } return res }
这种方法对于单个文件,代码量不多,其实也是可以的,但是对于一个庞大的项目,这种方法就显得很麻烦,特别是生产,我们难道为了测试去更改main函数,然后再重启,显然是不合理的。然后所有的测试业务都写在main函数,不利于管理,代码量一多就会变得复杂
3、单位测试入门
testing是golang里的一个轻量级的测试框架,可以用于单位测试,也可以用于性能测试,程序员可以基于这个框架写相应的单位测试用例,然后通过日志打印等方式进行debug调试,定位排查问题
使用很容易,基于原来的文件,sample.go,新建一个sample_test.do的测试文件,注意,必须命名为*_test.do
package main import ( "testing" ) func TestAdd(t *testing.T) { res := add(1,2) if res != 3 { t.Fatalf("error res %v" , res) } t.Logf("success res %v" , res) } func TestAddUpper(t *testing.T) { res := addUpper(10) if res != 55 { t.Fatalf("error res %v" , res) } t.Logf("success res %v" , res) }
注意:
测试用例文件必须以_test.go结尾
测试用例函数必须以Test开头,一般是Test+被测试的函数名
测试函数形式参数必须是*testing.T
出现错误,t.Fatalf打印错误日志并退出程序
程序正常,t.Logf打印格式化的日志
测试单个文件,要加上测试的原文件go test -v sample_test.go sample.go
测试单个函数,加上方法名,go test -v sample_test.go TestAdd
运行测试用例命令
cmd > go test :运行正确不打印日志,错误才会打印日志
cmd > go test -v :运行错误或成功,都打印日志
Golang
单元测试的基础今天就暂时到这里面,下面几个testing
包的常用断言方法.
// 输出测试日志
t.Logf()
t.Logf()
// 标记错误,但仍然执行后面的语句
t.Fail()
// 获取是否当前用例是执行错误的
t.Failed()
// 错误输出,等于 t.Logf 再执行 t.Fail()
t.Errorf("%s", "run ErrorF")
// 标记函数错误,并中断后面的执行
t.FailNow()
// 致命错误输出,等同于调用了 t.Logf 然后调用 t.FailNow()
t.Fatalf("%s", "run Fatelf")
————————————————
go test [-c] [-i] [build flags] [packages] [flags for test binary]
测试覆盖率
测试覆盖率是你的代码被测试套件覆盖的百分比。通常我们使用的都是语句的覆盖率,也就是在测试中至少被运行一次的代码占总代码的比例。
Go提供内置功能来检查你的代码覆盖率。我们可以使用go test -cover来查看测试覆盖率。例如:
split $ go test -cover
PASS
coverage: 100.0% of statements
ok github.com/Q1mi/studygo/code_demo/test_demo/split 0.005s
从上面的结果可以看到我们的测试用例覆盖了100%的代码。
Go还提供了一个额外的-coverprofile参数,用来将覆盖率相关的记录信息输出到一个文件。例如:
split $ go test -cover -coverprofile=c.out
PASS
coverage: 100.0% of statements
ok github.com/Q1mi/studygo/code_demo/test_demo/split 0.005s
上面的命令会将覆盖率相关的信息输出到当前文件夹下面的c.out文件中,然后我们执行go tool cover -html=c.out,使用cover工具来处理生成的记录信息,该命令会打开本地的浏览器窗口生成一个HTML报告。
go test整个项目报错:
golang 运行 go test 时 遇到报错 call has possible formatting directive %v 怎么解决
问题
Println call has possible formatting directive %v
go test 中不能使用 fmt.PrintLn(“%v”, v)
方案
使用 fmt.Printf(“%+v”, v)
fmt.Println("Hello, playground %d",i)
那么会出现warning:Println call has possible formatting directive %d Go vet exited.
fmt.Println doesn't do formatting things like %d. Instead, it uses the default format of its arguments, and adds spaces between them.
fmt.Println("Hello, playground",i) // Hello, playground 5
If you want printf style formatting, use fmt.Printf.
fmt.Printf("Hello, playground %d\n",i)
And you don't need to be particular about the type. %v will generally figure it out.
fmt.Printf("Hello, playground %v\n",i)
————————————————
原文链接:https://blog.csdn.net/Hack_Bug/article/details/110944701
Test注意要点
- Go的test不会保证多个TestXxx顺序执行,但是通常会按顺序执行
- 使用t.Run来执行subtests可以做到控制test输出以及test的顺序
// 顺序执行
func TestPrintSub(t *testing.T){
t.Run("a1",func(t *testing.T){fmt.Println("a1")})
t.Run("a2",func(t *testing.T){fmt.Println("a2")})
t.Run("a3",func(t *testing.T){fmt.Println("a3")})
}
- 使用TestMain作为初始化test,并且使用m.Run()来调用其他tests可以完成一些需要初始化操作的testing,
- 比如数据库连接,文件打开,Rest服务登录等
// TestMain
func TestMain(m *testing.M){
fmt.Println("test main first")
m.Run()
}
- 如果没有在TestMain中调用m.Run()则除了TestMain以外的其他tests都不好被执行
package split import ( "reflect" "testing" ) func TestSplit(t *testing.T) { type args struct { s string sep string } tests := []struct { name string args args wantResult []string }{ // TODO: Add test cases. {name: "test1",args: args{s:"a:b:c",sep: ":"},wantResult: []string{"a", "b", "c"}}, {name: "test2",args: args{s:"a:b:c",sep: ","},wantResult: []string{"a:b:c"}}, {name: "test3",args: args{s:"abcd",sep: "bc"},wantResult: []string{"a", "d"}}, {name: "test4",args: args{s:"沙河有沙又有河",sep: "沙"},wantResult: []string{"河有", "又有河"}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if gotResult := Split(tt.args.s, tt.args.sep); !reflect.DeepEqual(gotResult, tt.wantResult) { t.Errorf("Split() = %v, want %v", gotResult, tt.wantResult) } }) } }
链接:https://www.jianshu.com/p/3cf66a256d19
ubtests and Sub-benchmarks
T和B类型中的Run可以定义子测试和子benchmarks,不需要为sub们定义函数。This enables uses like table-driven benchmarks and creating hierarchical tests. It also provides a way to share common setup and tear-down code:(这个可以使用表驱动benchmark和层级式测试,也能提供公共setup和tear-down代码)
func TestFoo(t *testing.T) {
// <setup code>
t.Run("A=1", func(t *testing.T) { ... })
t.Run("A=2", func(t *testing.T) { ... })
t.Run("B=1", func(t *testing.T) { ... })
// <tear-down code>
}
运行测试时,可以-run和-bench flags设置运行那些测试:
go test -run '' # 运行所有测试
go test -run Foo # 运行一级测试,匹配 "Foo", 例如 "TestFooBar".
go test -run Foo/A= # 运行一级测试,匹配 "Foo", 运行二级测试,匹配 "A=".
go test -run /A=1 # 运行所有一级测试, 运行二级测试,匹配 "A=1".
子用例可以并行运行,父级仅在所有子用例运行完才完成。
func TestGroupedParallel(t *testing.T) {
for _, tc := range tests {
tc := tc // capture range variable
t.Run(tc.Name, func(t *testing.T) {
t.Parallel()
...
})
}
}
子测试间是并行的运行。
Main
有时需要在测试前或测试后有一些设置或tear-down的控制,或者控制那些代码需要在主线程运程,可以在一个文件内添加一个
func TestMain(m *testing.M)
————————————————
原文链接:https://blog.csdn.net/huafable007/article/details/109596821