测试
测试是自动化测试的简称,即编写简单的程序来确保程序(产品代码)在测试中针对特定输入产生预期的输出。Go的测试方法看上去相对比较低级,它依赖于命令go test和一些能用go test运行的测试函数的编写约定
go test 工具
go test 子命令是Go语言包的测试驱动程序,这些包根据某些约定组织在一起。在一个包目录中,以_test.go结尾的文件不是go build命令编译的目标,而是go test编译的目标。
在*_test.go文件中,三种函数需要特殊对待,即
- 功能测试函数
- 基准测试函数 :名称以benchmark开头,用来检测某些操作的性能。
- 示例运行测试函数 :以Example来测试某些操作的性能。用来提供机器检查过的文档。
Test 函数
每一个测试文件必须导入testing包,签名如下
import "testing" func TestName(t *testing.T) { }
功能测试函数必须以Test开头,可选的后缀名称必须以大写字母开头:
import "testing" func TestSin(t *testing.T) { } func TestCos(t *testing.T) { }
示例
如下编译一个测试连接mysql的单元测试,必须保证同目录的情况下,否则找不到,创建一个mysql.go用来编写连接的代码
package mysql import ( "database/sql" _ "github.com/go-sql-driver/mysql" ) func findByPk(pk int) int { var num int = 0 db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/plugin?charset=utf8") if err != nil { panic(err.Error()) } defer db.Close() stmtOut, err := db.Prepare("select id from t_admin where id=?") if err != nil { panic(err.Error()) } defer stmtOut.Close() err = stmtOut.QueryRow(pk).Scan(&num) if err != nil { panic(err.Error()) } return num }
在同目录下创建一个mysql_test.go用来编写测试代码
package mysql import ( "testing" ) func Test_findByPk(t *testing.T) { num := findByPk(1) t.Log(num) }
运行
- go test: 在当前目录下运行所有测试用例
- go test -file *_test.go : 运行指定测试文件。如 go test -file mysql_test.go
- go test -V: 显示详细结果
- go test -run="Test_XXX" : 指定运行某个方法
测试覆盖率
一个测试套件覆盖待测试包的比例称为测试的覆盖率。覆盖率无法直接通过数量来衡量。任何事情都是动态的。即使最微小的程序都无法精准的测量。语句覆盖率是指部分语句在一次执行中至少执行一次。go语言中提供cover工具
# go test -cover PASS coverage: 100.0% of statements ok jdGoBackend/library/sdk/jd 0.017s
go还提供了将输出打印报告的方法。
# go test -cover -coverprofile=c.out
然后可以通过html方法显示,使用cover
工具来处理生成的记录信息,该命令会打开本地的浏览器窗口生成一个HTML报告。
# go tool cover -html=c.out
基准测试
基准测试是在一定的工作负载之下检测程序性能的一种方法,在Go里面,基准测试函数看上去像一个测试函数。但前缀是Benchmark并且拥有一个*testing.B参数用来提供大多数和*testing.T相同的方法。额外增加一些与性能检测相关的方法。还提供了一个整数检测成员N,用来检测执行的次数。
基本格式
func BenchmarkName(b *testing.B){ // ... }
下面的基准测试在一个循环中调用了IsPaling共 N次
import "testing" func BenchmarkIsPaling(b *testing.B) { for i:=0; i< b.N; i++{ IsPaling("aaaaa") } }
和测试不同的是,默认情况下不会执行任何基准测试,需要标记-bench的参数指定要运行的基准参数。
# go test -bench=IsPaling BenchmarkIsPaling-8 10000000 203 ns/op PASS ok jdGoBackend/library/sdk/jd 2.255s
就上面的运行结果,基础名称BenchmarkIsPaling-8后缀8表示GOMAXPROCS的值。这个数字对并发基准测试很重要。
报告告诉我们每次IsPaling调用耗费0.23ms。这个是1 000 000 0次调用的平均值
我们可以通过-benchmen再报告中包含了内存分配统计数据。这里和优化之前的内存分配进行比较。
# go test -bench=. -benchmen // . 通配符匹配所有 PASS BenchmarkIsPaling 1000000 1026 ns/op 304 B/op 4 allocs/op
最后,性能比较函数知识普通的代码,它们的表现形式通常是带有一个参数的函数,被多个不同Benchmark函数传入不同的值来调用。如下
func benchmark(b *testing.B, size int){/* ... */} func Benchmark10(b *testing.B){ benchmark(b, 10) } func Benchmark100(b *testing.B){ benchmark(b, 100) } func Benchmark1000(b *testing.B){ benchmark(b, 1000) }
参数size指定了输入的大小,每个Benchmark函数传入的值都不同但是在每个函数内部是一个常量,不要使用b.N作为输入的大小。除非把它当作固定大小输入的循环次数。否则该基准测试的结果毫无意义。
最后测试牵扯到性能,将会在性能模块详细介绍。