mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2024-12-25 14:28:58 +00:00
Update ch11-02.md
This commit is contained in:
parent
0245b3366e
commit
28dd62b2c2
@ -136,7 +136,87 @@ exit status 1
|
||||
FAIL gopl.io/ch11/word1 0.014s
|
||||
```
|
||||
|
||||
TODO
|
||||
|
||||
当然, 一旦我们已经修复了失败的测试用例, 在我们提交代码更新之前, 我们应该以不带参数的 `go test` 命令运行全部的测试用例, 以确保更新没有引入新的问题.
|
||||
|
||||
我们现在的任务就是修复这些错误. 简要分析后发现第一个BUG的原因是我们采用了 byte 而不是 rune 序列, 所以像 "été" 中的 é 等非 ASCII 字符不能正确处理. 第二个BUG是因为没有忽略空格和字母的大小写导致的.
|
||||
|
||||
针对上述两个BUG, 我们仔细重写了函数:
|
||||
|
||||
```Go
|
||||
gopl.io/ch11/word2
|
||||
// Package word provides utilities for word games.
|
||||
package word
|
||||
|
||||
import "unicode"
|
||||
|
||||
// IsPalindrome reports whether s reads the same forward and backward.
|
||||
// Letter case is ignored, as are non-letters.
|
||||
func IsPalindrome(s string) bool {
|
||||
var letters []rune
|
||||
for _, r := range s {
|
||||
if unicode.IsLetter(r) {
|
||||
letters = append(letters, unicode.ToLower(r))
|
||||
}
|
||||
}
|
||||
for i := range letters {
|
||||
if letters[i] != letters[len(letters)-1-i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
```
|
||||
|
||||
同时我们也将之前的所有测试数据合并到了一个测试中的表格中.
|
||||
|
||||
```Go
|
||||
func TestIsPalindrome(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input string
|
||||
want bool
|
||||
}{
|
||||
{"", true},
|
||||
{"a", true},
|
||||
{"aa", true},
|
||||
{"ab", false},
|
||||
{"kayak", true},
|
||||
{"detartrated", true},
|
||||
{"A man, a plan, a canal: Panama", true},
|
||||
{"Evil I did dwell; lewd did I live.", true},
|
||||
{"Able was I ere I saw Elba", true},
|
||||
{"été", true},
|
||||
{"Et se resservir, ivresse reste.", true},
|
||||
{"palindrome", false}, // non-palindrome
|
||||
{"desserts", false}, // semi-palindrome
|
||||
}
|
||||
for _, test := range tests {
|
||||
if got := IsPalindrome(test.input); got != test.want {
|
||||
t.Errorf("IsPalindrome(%q) = %v", test.input, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
我们的新测试阿都通过了:
|
||||
|
||||
```
|
||||
$ go test gopl.io/ch11/word2
|
||||
ok gopl.io/ch11/word2 0.015s
|
||||
```
|
||||
|
||||
这种表格驱动的测试在Go中很常见的. 我们很容易想表格添加新的测试数据, 并且后面的测试逻辑也没有冗余, 这样我们可以更好地完善错误信息.
|
||||
|
||||
失败的测试的输出并不包括调用 t.Errorf 时刻的堆栈调用信息. 不像其他语言或测试框架的 assert 断言, t.Errorf 调用也没有引起 panic 或停止测试的执行. 即使表格中前面的数据导致了测试的失败, 表格后面的测试数据依然会运行测试, 因此在一个测试中我们可能了解多个失败的信息.
|
||||
|
||||
如果我们真的需要停止测试, 或许是因为初始化失败或可能是早先的错误导致了后续错误等原因, 我们可以使用 t.Fatal 或 t.Fatalf 停止测试. 它们必须在和测试函数同一个 goroutine 内调用.
|
||||
|
||||
测试失败的信息一般的形式是 "f(x) = y, want z", f(x) 解释了失败的操作和对应的输出, y 是实际的运行结果, z 是期望的正确的结果. 就像前面检查回文字符串的例子, 实际的函数用于 f(x) 部分. 如果显示 x 是表格驱动型测试中比较重要的部分, 因为同一个断言可能对应不同的表格项执行多次. 要避免无用和冗余的信息. 在测试类似 IsPalindrome 返回布尔类型的函数时, 可以忽略并没有额外信息的 z 部分. 如果 x, y 或 z 是 y 的长度, 输出一个相关部分的简明总结即可. 测试的作者应该要努力帮助程序员诊断失败的测试.
|
||||
|
||||
**练习 11.1:** 为 4.3节 中的 charcount 程序编写测试.
|
||||
|
||||
**练习 11.2:** 为 (§6.5)的 IntSet 编写一组测试, 用于检查每个操作后的行为和基于内置 map 的集合等价 , 后面 练习11.7 将会用到.
|
||||
|
||||
|
||||
{% include "./ch11-02-1.md" %}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user