mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2024-12-25 22:38:56 +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
|
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" %}
|
{% include "./ch11-02-1.md" %}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user