Go语言精进之路:从新手到高手的编程思想、方法和技巧(2)
上QQ阅读APP看书,第一时间看更新

42.2 表驱动的测试实践

Go测试代码的逻辑十分简单,约束也甚少,但我们发现:上面仅有三组预置输入数据的示例的测试代码已显得十分冗长,如果为测试预置的数据组数增多,测试函数本身就将变得十分庞大。并且,我们看到上述示例的测试逻辑中存在很多重复的代码,显得十分烦琐。我们来尝试对上述示例做一些改进:

// chapter8/sources/table_driven_strings_test.go
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)
        }
    }
}

在上面这个改进的示例中,我们将之前示例中重复的测试逻辑合并为一个,并将预置的输入数据放入一个自定义结构体类型的切片中。这个示例的长度看似并没有比之前的实例缩减多少,但它却是一个可扩展的测试设计。如果增加输入测试数据的组数,就像下面这样:

// chapter8/sources/table_driven_strings_more_cases_test.go
func TestCompare(t *testing.T) {
    compareTests := []struct {
        a, b string
        i    int
    }{
        {"", "", 0},
        {"a", "", 1},
        {"", "a", -1},
        {"abc", "abc", 0},
        {"ab", "abc", -1},
        {"abc", "ab", 1},
        {"x", "ab", 1},
        {"ab", "x", -1},
        {"x", "a", 1},
        {"b", "x", -1},
        {"abcdefgh", "abcdefgh", 0},
        {"abcdefghi", "abcdefghi", 0},
        {"abcdefghi", "abcdefghj", -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)
        }
    }
}

可以看到,无须改动后面的测试逻辑,只需在切片中增加数据条目即可。在这种测试设计中,这个自定义结构体类型的切片(上述示例中的compareTests)就是一个(自定义结构体类型的字段就是列),而基于这个数据表的测试设计和实现则被称为“表驱动的测试”