mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-19 20:24:20 +08:00
make
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
### 11.2.5. 編寫有效的測試
|
||||
|
||||
|
||||
许多Go新人会惊异与它的极简的测试框架. 很多其他语言的测试框架都提供了识别测试函数的机制(通常使用反射或元数据), 通过设置一些 ‘‘setup’’ 和 ‘‘teardown’’ 的钩子函数来执行测试用例运行的初始化或之后的清理操作, 同时测试工具箱还提供了很多类似assert断言, 比较值, 格式化输出错误信息和停止一个识别的测试等辅助函数(通常使用异常机制). 虽然这些机制可以使得测试非常简洁, 但是测试输出的日志却像火星文一般难以理解. 此外, 虽然测试最终也会输出 PASS 或 FAIL 的报告, 但是它们提供的信息格式却非常不利于代码维护者快速定位问题, 因为失败的信息的具体含义是非常隐患的, 比如 "assert: 0 == 1" 或 成页的海量跟踪日志.
|
||||
許多Go新人會驚異與它的極簡的測試框架. 很多其他語言的測試框架都提供了識彆測試函數的機製(通常使用反射或元數據), 通過設置一些 ‘‘setup’’ 和 ‘‘teardown’’ 的鈎子函數來執行測試用例運行的初始化或之後的清理操作, 衕時測試工具箱還提供了很多類似assert斷言, 比較值, 格式化輸齣錯誤信息和停止一個識彆的測試等輔助函數(通常使用異常機製). 雖然這些機製可以使得測試非常簡潔, 但是測試輸齣的日誌卻像火星文一般難以理解. 此外, 雖然測試最終也會輸齣 PASS 或 FAIL 的報告, 但是它們提供的信息格式卻非常不利於代碼維護者快速定位問題, 因為失敗的信息的具體含義是非常隱患的, 比如 "assert: 0 == 1" 或 成頁的海量跟蹤日誌.
|
||||
|
||||
Go语言的测试风格则形成鲜明对比. 它期望测试者自己完成大部分的工作, 定义函数避免重复, 就像普通编程那样. 编写测试并不是一个机械的填充过程; 一个测试也有自己的接口, 尽管它的维护者也是测试仅有的一个用户. 一个好的测试不应该引发其他无关的错误信息, 它只要清晰简洁地描述问题的症状即可, 有时候可能还需要一些上下文信息. 在理想情况下, 维护者可以在不看代码的情况下就能根据错误信息定位错误产生的原因. 一个好的测试不应该在遇到一点小错误就立刻退出测试, 它应该尝试报告更多的测试, 因此我们可能从多个失败测试的模式中发现错误产生的规律.
|
||||
Go語言的測試風格則形成鮮明對比. 它期望測試者自己完成大部分的工作, 定義函數避免重復, 就像普通編程那樣. 編寫測試並不是一個機械的填充過程; 一個測試也有自己的接口, 盡管它的維護者也是測試僅有的一個用戶. 一個好的測試不應該引發其他無關的錯誤信息, 它隻要清晰簡潔地描述問題的癥狀卽可, 有時候可能還需要一些上下文信息. 在理想情況下, 維護者可以在不看代碼的情況下就能根據錯誤信息定位錯誤產生的原因. 一個好的測試不應該在遇到一點小錯誤就立刻退齣測試, 它應該嘗試報告更多的測試, 因此我們可能從多個失敗測試的模式中發現錯誤產生的規律.
|
||||
|
||||
下面的断言函数比较两个值, 然后生成一个通用的错误信息, 并停止程序. 它很方便使用也确实有效果, 但是当识别的时候, 错误时打印的信息几乎是没有价值的. 它并没有为解决问题提供一个很好的入口.
|
||||
下麪的斷言函數比較兩個值, 然後生成一個通用的錯誤信息, 並停止程序. 它很方便使用也確實有效果, 但是噹識彆的時候, 錯誤時打印的信息幾乎是沒有價值的. 它並沒有為解決問題提供一個很好的入口.
|
||||
|
||||
```Go
|
||||
import (
|
||||
@@ -26,7 +26,7 @@ func TestSplit(t *testing.T) {
|
||||
}
|
||||
```
|
||||
|
||||
从这个意义上说, 断言函数犯了过早抽象的错误: 仅仅测试两个整数是否相同, 而放弃了根据上下文提供更有意义的错误信息的做法. 我们可以根据具体的错误打印一个更有价值的错误信息, 就像下面例子那样. 测试在只有一次重复的模式出现时引入抽象.
|
||||
從這個意義上説, 斷言函數犯了過早抽象的錯誤: 僅僅測試兩個整數是否相衕, 而放棄了根據上下文提供更有意義的錯誤信息的做法. 我們可以根據具體的錯誤打印一個更有價值的錯誤信息, 就像下麪例子那樣. 測試在隻有一次重復的模式齣現時引入抽象.
|
||||
|
||||
```Go
|
||||
func TestSplit(t *testing.T) {
|
||||
@@ -40,10 +40,10 @@ func TestSplit(t *testing.T) {
|
||||
}
|
||||
```
|
||||
|
||||
现在的测试不仅报告了调用的具体函数, 它的输入, 和结果的意义; 并且打印的真实返回的值和期望返回的值; 并且即使断言失败依然会继续尝试运行更多的测试. 一旦我们写了这样结构的测试, 下一步自然不是用更多的if语句来扩展测试用例, 我们可以用像 IsPalindrome 的表驱动测试那样来准备更多的 s, sep 测试用例.
|
||||
現在的測試不僅報告了調用的具體函數, 它的輸入, 和結果的意義; 並且打印的眞實返迴的值和期望返迴的值; 並且卽使斷言失敗依然會繼續嘗試運行更多的測試. 一旦我們寫了這樣結構的測試, 下一步自然不是用更多的if語句來擴展測試用例, 我們可以用像 IsPalindrome 的錶驅動測試那樣來準備更多的 s, sep 測試用例.
|
||||
|
||||
前面的例子并不需要额外的辅助函数, 如果如果有可以使测试代码更简单的方法我们也乐意接受. (我们将在 13.3节 看到一个 reflect.DeepEqual 辅助函数.) 开始一个好的测试的关键是通过实现你真正想要的具体行为, 然后才是考虑然后简化测试代码. 最好的结果是直接从库的抽象接口开始, 针对公共接口编写一些测试函数.
|
||||
前麪的例子並不需要額外的輔助函數, 如果如果有可以使測試代碼更簡單的方法我們也樂意接受. (我們將在 13.3節 看到一個 reflect.DeepEqual 輔助函數.) 開始一個好的測試的關鍵是通過實現你眞正想要的具體行為, 然後纔是考慮然後簡化測試代碼. 最好的結果是直接從庫的抽象接口開始, 針對公共接口編寫一些測試函數.
|
||||
|
||||
**练习11.5:** 用表格驱动的技术扩展TestSplit测试, 并打印期望的输出结果.
|
||||
**練習11.5:** 用錶格驅動的技術擴展TestSplit測試, 並打印期望的輸齣結果.
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user