传统测试
- 测试数据和测试逻辑混在一起
- 出错信息不明确
- 一旦一个数据出错测试全部结束
表格驱动测试
- 分离的测试数据和测试逻辑
- 明确的出错信息
- 可以部分失败
- go语言的语法可以让我们更容易的实践表格驱动测试
tests := []struct {
a, b, c int32
}{
{1, 2, 3},
{0, 2, 2},
{0, 0, 0},
{-1, 1, 0},
{math.MaxInt32, 1, math.MinInt32},
}
for _, test := range tests {
if actual := add(test.a, test,b); actual != test.c {
// 报错
}
开始进行测试
这里我通过一个leetcode中的题目为例,对解答做表格驱动测试 无重复字符的最长子串
func lengthOfNonRepeatingSubStr(s string) int {
lastOccurred := make(map[rune]int) // 使用map记录某个字符上一次出现的位置
start := 0 // 记录没有出现重复字符的字符串的最开始的位置
maxLength := 0 // 没有出现重复字符的字符串的最长长度
for i, ch := range []rune(s) { // 循环读取byte类型的数据,使用rune更好
// 如果之前已经读取过相同字符的位置,并且该字符上一次出现的位置在start后边,则需要更新start保证字符串没有重复字符
if lastI, ok := lastOccurred[ch]; ok && lastI >= start {
start = lastI + 1
}
// 对比当前没有重复字符的字符串的长度是否超过当前的最长长度,超过则更新
if i-start+1 > maxLength {
maxLength = i - start + 1
}
// 将当前字符ch的位置i更新到lastOccurred这个map中
lastOccurred[ch] = i
}
return maxLength
}
借助Golang以int32保存Unicode字符集的特性,我们甚至可以用上面的代码来判断类似一二三二一这种的中文字符串的不重复最长字串,只需要使用rune类型来作为map的key。言归正传,我们通过表格驱动测试来对上面对这段代码来测试一下,看看我们能否AC呢?
# main_test.go
func TestNoRepeat(t *testing.T) {
tests := []struct {
s string
len int
}{
{"abcabcbb", 3},
{"bbbbb", 1},
{"一二三二一", 5},
}
for _, tt := range tests {
if actual := lengthOfNonRepeatingSubStr(tt.s); actual != tt.len {
t.Errorf("lengthOfNonRepeatingSubStr(%s);"+
"got %d; expected %d",
tt.s, actual, tt.len)
}
}
}
Golang可以通过以xxx_test.go这种明明方法为xxx.go提供一个测试文件。
tests := []struct {
s string
len int
}{
{"abcabcbb", 3},
{"bbbbb", 1},
{"一二三二一", 5},
}
在这段代码中,我们通过变量tests定义了测试用例,包括输入参数和预期结果。
for _, tt := range tests {
if actual := lengthOfNonRepeatingSubStr(tt.s); actual != tt.len {
t.Errorf("lengthOfNonRepeatingSubStr(%s);"+
"got %d; expected %d",
tt.s, actual, tt.len)
}
}
然后,我们使用测试用例来测试一下,测试的方式类似上边这样,通过循环每个用例来判断输入、输出和对应的预期结果是否相符,从而达到测试效果。
=== RUN TestNoRepeat
--- PASS: TestNoRepeat (0.00s)
PASS