介绍
在go1.7之后,testing包T和B的引入了一个Run方法,用于创建subtests 和 sub-benchmarks. subtests 和 sub-benchmarks可以让开发者更好的处理测试中的失败,更好的控制运行哪个测试用例,控制并行测试操作,测试代码更加简洁和可维护性更强。
Table-driven tests 基础
首先我们先讨论下Go中常见的测试代码编写方式。
一系列相关的测试校验可以通过遍历测试用例的切片来实现,代码如下:
func TestTime(t *testing.T) {
testCases := []struct {
gmt string
loc string
want string
}{
{
"12:31", "Europe/Zuri", "13:31"}, // incorrect location name
{
"12:31", "America/New_York", "7:31"}, // should be 07:31
{
"08:08", "Australia/Sydney", "18:08"},
}
for _, tc := range testCases {
loc, err := time.LoadLocation(tc.loc)
if err != nil {
t.Fatalf("could not load location %q", tc.loc)
}
gmt, _ := time.Parse("15:04", tc.gmt)
if got := gmt.In(loc).Format("15:04"); got != tc.want {
t.Errorf("In(%s, %s) = %s; want %s", tc.gmt, tc.loc, got, tc.want)
}
}
}
测试函数必须以Test开头,Test后跟的名字也必须首字母大写。
上面的测试方式称为table-driven 测试法,可以降低重复代码。
Table-driven benchmarks
在go1.7之前是不能够对benchmarks采用table-driven的方法的,如果要测试不同的参数就需要编写不同的benchmark函数,在go1.7之前常见的benchmarks测试代码如下:
func benchmarkAppendFloat(b *testing.B, f float64, fmt byte, prec, bitSize int) {
dst := make([]byte, 30)
b.ResetTimer() // Overkill here, but for illustrative purposes.
for i := 0; i < b.N; i++ {
AppendFloat(dst[:0], f, fmt, prec, bitSize)
}
}
func BenchmarkAppendFloatDecimal(b *testing.B) { benchmarkAppendFloat(b, 33909, 'g', -1, 64) }
func BenchmarkAppendFloat(b *testing.B) { benchmarkAppendFloat(b, 339.7784, 'g', -1, 64) }
func BenchmarkAppendFloatExp(b *testing.B) { benchmarkAppendFloat(b, -5.09e75, 'g', -1, 64) }
func BenchmarkAppendFloatNegExp(b *testing.B) { benchmarkAppendFloat(b, -5.11e-95, 'g', -1, 64) }
func BenchmarkAppendFloatBig(b *testing.B) { benchmarkAppendFloat(b, 123456789123456789123456789, 'g', -1, 64) }
go1.7之后,采用table-drive方法代码如下:
func BenchmarkAppendFloat(b *testing.B) {
benchmarks := []struct{
name string
float float64
fmt byte
prec int
bitSize int
}{
{
"Decimal", 33909, 'g', -1, 64},
{
"Float", 339.7784, 'g', -1, 64},
{
"Exp", -5.09e75, 'g', -1, 64},
{
"NegExp", -5.11e-95, 'g', -1, 64},
{
"Big", 123456789123456789123456789, 'g', -1, 64},
...
}
dst := make([]byte, 30)
for _, bm := range benchmarks {
b.Run(bm.name, func(b *testing.B) {