Go语言精进之路读书笔记第42条——优先编写表驱动的测试
聚焦测试函数的内部代码该如何编写。
42.1 Go测试代码的一般逻辑
针对给定的输入数据,比较被测函数/方法返回的实际结果值与预期值,如有差异,则通过testing包提供的相关函数输出差异信息
42.2 表驱动的测试实践
func TestCompare(t *testing.T) {
compareTests := []struct {
a, b string
i int
}{
{"", "", 0},
{"a", "", 1},
{"", "a", -1},
}
for _, tt := range compareTests {
cmp := strings.Compare(tt.a, tt.b)
if cmp != tt.i {
t.Errorf(`want %v, but Compare(%q, %q) = %v`, tt.i, tt.a, tt.b, cmp)
}
}
}
42.3 表驱动测试的优点
(1) 简单和紧凑,表驱动测试将不同测试项经由被测目标执行后的实际输出结果与预期结果的差异判断合并为一个
(2) 数据即测试,扩散输入数据集即扩散测试
(3) 结合子测试后,可单独运行某个数据项的测试
func TestCompare(t *testing.T) {
compareTests := []struct {
name, a, b string
i int
}{
{"compareTwoEmptyString", "", "", 7},
{"compareSecondStringEmpty", "a", "", 6},
{"compareFirstStringEmpty", "", "a", -1},
}
for _, tt := range compareTests {
cmp := strings.Compare(tt.a, tt.b)
if cmp != tt.i {
t.Errorf(`[%s] want %v, but Compare(%q, %q) = %v`, tt.name, tt.i, tt.a, tt.b, cmp)
}
}
}
42.4 表驱动测试实践中的注意事项
1.表的实现方式
测试中使用的表是用自定义结构体的切片实现,也可以使用基于自定义结构体的其他集合类型(如map)来实现,但要注意map集合类型进行迭代所返回的集合中的元素顺序是不确定的。
2.测试失败时的数据项的定位
在自定义结构体中添加一个name字段来区分不同的数据项,并在测试结果输出该name字段以在测试失败时辅助快速定位问题数据。
3.Errorf还是Fatalf
- Error/Errorf会继续执行该goroutine后续的测试
- Fatal/Fatalf会立刻停止当前goroutine的测试执行