This commit is contained in:
chai2010
2015-12-16 10:55:14 +08:00
parent 3e80d25c15
commit c187ed90f8
6 changed files with 60 additions and 41 deletions

View File

@@ -1,9 +1,9 @@
### 11.2.2. 測試一個命令
对于测试`go test` 是一的有用的工具, 但是稍加努力我也可以用它来测试可执行程序. 如果一包的名字是 main, 那么在构建时会生成一个可执行程序, 不 main 包可以作为一个包被测试器代码导入.
對於測試`go test` 是一的有用的工具, 但是稍加努力我也可以用它來測試可執行程序. 如果一包的名字是 main, 那麽在構建時會生成一個可執行程序, 不 main 包可以作為一個包被測試器代碼導入.
让我们为 2.3.2 的 echo 程序编写一个测试. 我们先将程序拆分为两个函数: echo 函完成正的工作, main 函数用于处理命令行输入参数和echo可能返回的错误.
讓我們為 2.3.2 的 echo 程序編寫一個測試. 我們先將程序拆分為兩個函數: echo 函完成正的工作, main 函數用於處理命令行輸入參數和echo可能返迴的錯誤.
```Go
gopl.io/ch11/echo
@@ -42,7 +42,7 @@ func echo(newline bool, sep string, args []string) error {
}
```
测试中吗我们可以用各种参数和标标志调用 echo 函, 然后检测它的输出是否正, 我们通过增加参数来减少 echo 函数对全局变量的依. 我们还增加了一个全局名为 out 的变量来替代直接使用 os.Stdout, 这样测试代码可以根需要 out 修改为不同的对象以便于检查. 下就是 echo_test.go 文件中的测试代码:
測試中嗎我們可以用各種參數和標標誌調用 echo 函, 然後檢測它的輸齣是否正, 我們通過增加參數來減少 echo 函數對全侷變量的依. 我們還增加了一個全侷名為 out 的變量來替代直接使用 os.Stdout, 這樣測試代碼可以根需要 out 脩改為不衕的對象以便於檢査. 下就是 echo_test.go 文件中的測試代碼:
```Go
package main
@@ -83,15 +83,15 @@ func TestEcho(t *testing.T) {
}
```
意的是测试代码和产品代码在同一个包. 然是main包, 也有对应的 main 入口函, 但是在测试的时候 main 包是 TestEcho 测试函数导入的一普通包, 里面 main 函数并没有被导出是被忽略的.
意的是測試代碼和產品代碼在衕一個包. 然是main包, 也有對應的 main 入口函, 但是在測試的時候 main 包是 TestEcho 測試函數導入的一普通包, 裡麪 main 函數並沒有被導齣是被忽略的.
过将测试放到格中, 我很容易添加新的测试用例. 我通增加下面的测试用例看看失的情是怎么样的:
過將測試放到格中, 我很容易添加新的測試用例. 我通增加下麪的測試用例看看失的情是怎麽樣的:
```Go
{true, ",", []string{"a", "b", "c"}, "a b c\n"}, // NOTE: wrong expectation!
```
`go test` 输出如下:
`go test` 輸齣如下:
```
$ go test gopl.io/ch11/echo
@@ -101,8 +101,8 @@ FAIL
FAIL gopl.io/ch11/echo 0.006s
```
错误信息描述了尝试的操作(使用Go类似语法), 实际的行, 和期望的行. 通过这样的错误信息, 你可以在检视代码之前就很容易定位错误的原因.
錯誤信息描述了嘗試的操作(使用Go類似語法), 實際的行, 和期望的行. 通過這樣的錯誤信息, 你可以在檢視代碼之前就很容易定位錯誤的原因.
意的是在测试代码中并没有调用 log.Fatal 或 os.Exit, 因为调用这类函数会导致程序提前退; 调用这些函的特权应该放在 main 函中. 如果的有以外的事情导致函数发送 panic, 测试驱动应该尝试 recover, 然后将当前测试当作失败处理. 如果是可期的错误, 例如非法的用户输入, 找不到文件, 或配置文件不当等应该通过返回一个非空的 error 的方式理. 幸运的是(上的意外是一个插曲), 我的 echo 示例是比较简单的也有需要返非空error的情.
意的是在測試代碼中並沒有調用 log.Fatal 或 os.Exit, 因為調用這類函數會導緻程序提前退; 調用這些函的特權應該放在 main 函中. 如果的有以外的事情導緻函數發送 panic, 測試驅動應該嘗試 recover, 然後將噹前測試噹作失敗處理. 如果是可期的錯誤, 例如非法的用戶輸入, 找不到文件, 或配置文件不噹等應該通過返迴一個非空的 error 的方式理. 倖運的是(上的意外是一個插麯), 我的 echo 示例是比較簡單的也有需要返非空error的情.

View File

@@ -1,15 +1,15 @@
### 11.2.3. 白盒測試
个测试分类的方法是基于测试者是否需要了解被测试对象的部工作原理. 黑盒测试只需要测试包公的文和API行, 内部实现对测试代码是透明的. 相反, 白盒测试有访问包内部函数和数据结构的权限, 因此可以做到一下普通客户端无法实现的测试. 例如, 一个饱和测试可以在每操作之后检测不变量的数据类型. (白盒测试只是一个传统的名, 其实称为 clear box 会更准确.)
個測試分類的方法是基於測試者是否需要了解被測試對象的部工作原理. 黑盒測試隻需要測試包公的文和API行, 內部實現對測試代碼是透明的. 相反, 白盒測試有訪問包內部函數和數據結構的權限, 因此可以做到一下普通客戶端無法實現的測試. 例如, 一個飽和測試可以在每操作之後檢測不變量的數據類型. (白盒測試隻是一個傳統的名, 其實稱為 clear box 會更準確.)
黑盒和白盒这两种测试方法是互的. 黑盒测试一般更健, 随着软件实现的完善测试代码很少需要更新. 它可以帮助测试者了解是客的需求, 可以帮助发现API设计的一些不足之. 相反, 白盒测试则可以对内部一些棘手的实现提供更多的测试覆盖.
黑盒和白盒這兩種測試方法是互的. 黑盒測試一般更健, 隨着軟件實現的完善測試代碼很少需要更新. 它可以幫助測試者了解是客的需求, 可以幫助發現API設計的一些不足之. 相反, 白盒測試則可以對內部一些棘手的實現提供更多的測試覆蓋.
们已经看到两种测试的例子. TestIsPalindrome 测试仅仅使用导出的 IsPalindrome 函, 因此它是一黑盒测试. TestEcho 测试则调用了部的 echo 函, 且更新了部的 out 全局变量, 这两个都是未导出的, 因此它是白盒测试.
們已經看到兩種測試的例子. TestIsPalindrome 測試僅僅使用導齣的 IsPalindrome 函, 因此它是一黑盒測試. TestEcho 測試則調用了部的 echo 函, 且更新了部的 out 全侷變量, 這兩個都是未導齣的, 因此它是白盒測試.
当我们开发TestEcho测试的时候, 我们修改了 echo 函使用包的 out 作为输出对象, 因此测试代码可以用另一个实现代替标准输出, 这样可以方便比 echo 的输出数据. 使用似的技, 我可以将产品代的其他部分也替换为一个容易测试的伪对象. 使用伪对象的好是我可以方便配置, 容易预测, 更可靠, 也更容易察. 同时也可以避免一些不良的副作用, 例如更新生产数据库或信用卡消费行为.
噹我們開發TestEcho測試的時候, 我們脩改了 echo 函使用包的 out 作為輸齣對象, 因此測試代碼可以用另一個實現代替標準輸齣, 這樣可以方便比 echo 的輸齣數據. 使用似的技, 我可以將產品代的其他部分也替換為一個容易測試的僞對象. 使用僞對象的好是我可以方便配置, 容易預測, 更可靠, 也更容易察. 衕時也可以避免一些不良的副作用, 例如更新生產數據庫或信用卡消費行為.
的代演示了为用户提供网络存储的web服中的配额检测逻辑. 当用户使用了超 90% 的存储配额之后将发送提醒件.
的代演示了為用戶提供網絡存儲的web服中的配額檢測邏輯. 噹用戶使用了超 90% 的存儲配額之後將發送提醒件.
```Go
gopl.io/ch11/storage1
@@ -50,7 +50,7 @@ func CheckQuota(username string) {
}
```
们想测试这个代码, 但是我们并不希望发送真实的邮件. 因此我们将邮件处理逻辑放到一个私有的 notifyUser 函.
們想測試這個代碼, 但是我們並不希望發送眞實的郵件. 因此我們將郵件處理邏輯放到一個俬有的 notifyUser 函.
```Go
gopl.io/ch11/storage2
@@ -76,7 +76,7 @@ func CheckQuota(username string) {
}
```
在我可以在测试中用伪邮件发送函替代真实的邮件发送函. 它只是简单记录要通知的用户和邮件的容.
在我可以在測試中用僞郵件發送函替代眞實的郵件發送函. 它隻是簡單記録要通知的用戶和郵件的容.
```Go
package storage
@@ -110,7 +110,7 @@ func TestCheckQuotaNotifiesUser(t *testing.T) {
}
```
这里有一个问题: 当测试函数返回后, CheckQuota 不能正常工作, 因 notifyUsers 依然使用的是测试函数的伪发送邮件函. (更新全局对象的时候总会有这种风险.) 我们必须修改测试代码恢复 notifyUsers 原先的状态以便后续其他的测试没有影, 要保所有的行路径后都能恢, 包括测试失败或 panic 情形. 在这种情况下, 我们建议使用 defer 理恢的代.
這裡有一個問題: 噹測試函數返迴後, CheckQuota 不能正常工作, 因 notifyUsers 依然使用的是測試函數的僞發送郵件函. (更新全侷對象的時候總會有這種風險.) 我們必鬚脩改測試代碼恢復 notifyUsers 原先的狀態以便後續其他的測試沒有影, 要保所有的行路徑後都能恢, 包括測試失敗或 panic 情形. 在這種情況下, 我們建議使用 defer 理恢的代.
```Go
func TestCheckQuotaNotifiesUser(t *testing.T) {
@@ -127,8 +127,8 @@ func TestCheckQuotaNotifiesUser(t *testing.T) {
}
```
这种处理模式可以用来暂时保存和恢所有的全局变量, 包括命令行标志参数, 调试选项, 和优化参数; 安和移除导致生产代码产生一些调试信息的子函; 有有些诱导生产代码进入某些重要状态的改, 比如 超, 错误, 甚至是一些刻意造的并发行为.
這種處理模式可以用來暫時保存和恢所有的全侷變量, 包括命令行標誌參數, 調試選項, 和優化參數; 安和移除導緻生產代碼產生一些調試信息的子函; 有有些誘導生產代碼進入某些重要狀態的改, 比如 超, 錯誤, 甚至是一些刻意造的並發行為.
这种方式使用全局变量是安全的, 因 go test 并不会同时并发地执行多个测试.
這種方式使用全侷變量是安全的, 因 go test 並不會衕時並發地執行多個測試.

View File

@@ -1,47 +1,60 @@
### 11.2.4. 擴展測試包
虑下这两个包: net/url 包, 提供了 URL 解析的功能; net/http 包, 提供了web服和HTTP客端的功能. 如我所料, 上的 net/http 包依赖下层的 net/url 包. 然, net/url 包中的一个测试是演示不URL和HTTP客端的交互行. 也就是, 一个下层包的测试代码导入了上的包.
慮下這兩個包: net/url 包, 提供了 URL 解析的功能; net/http 包, 提供了web服和HTTP客端的功能. 如我所料, 上的 net/http 包依賴下層的 net/url 包. 然, net/url 包中的一個測試是演示不URL和HTTP客端的交互行. 也就是, 一個下層包的測試代碼導入了上的包.
![](../images/ch11-01.png)
这样的行在 net/url 包的测试代码中会导致包的循环依赖, 正如 11.1中上箭所示, 同时正如我在 10.1节所说, Go语言规范是禁止包的循环依赖的.
這樣的行在 net/url 包的測試代碼中會導緻包的循環依賴, 正如 11.1中上箭所示, 衕時正如我在 10.1節所説, Go語言規範是禁止包的循環依賴的.
可以通过测试扩展包的方式解决循环依赖的问题, 也就是在 net/url 包所在的目录声明一 url_test 测试扩展包. 其中测试扩展包名的 `_test` 后缀告诉 go test 工具它应该建立一个额外的包来运行测试. 我们将这个扩展测试包的入路径视作是 net/url_test 更容易理解, 但实际上它不能被其他任何包入.
可以通過測試擴展包的方式解決循環依賴的問題, 也就是在 net/url 包所在的目録聲明一 url_test 測試擴展包. 其中測試擴展包名的 `_test` 後綴告訴 go test 工具它應該建立一個額外的包來運行測試. 我們將這個擴展測試包的入路徑視作是 net/url_test 更容易理解, 但實際上它不能被其他任何包入.
为测试扩展包是一个独立的包, 因此可以导入测试代码依赖的其他的助包; 包内的测试代码可能法做到. 在设计层面, 测试扩展包是在所以它依的包的上, 正如 11.2所示.
為測試擴展包是一個獨立的包, 因此可以導入測試代碼依賴的其他的助包; 包內的測試代碼可能法做到. 在設計層麪, 測試擴展包是在所以它依的包的上, 正如 11.2所示.
![](../images/ch11-02.png)
过回避循环导入依, 扩展测试包可以更活的测试, 特是集成测试(需要测试多个组件之的交互), 可以像普通用程序那自由地入其他包.
過迴避循環導入依, 擴展測試包可以更活的測試, 特是集成測試(需要測試多個組件之的交互), 可以像普通用程序那自由地入其他包.
可以用 go list 工具看包对应目录中哪些Go源文件是品代, 哪些是包内测试, 哪些测试扩展包. 我以 fmt 包作为一个例子. GoFiles 表示产品代码对应的Go源文件列; 也就是 go build 命令要编译的部分:
可以用 go list 工具看包對應目録中哪些Go源文件是品代, 哪些是包內測試, 哪些測試擴展包. 我以 fmt 包作為一個例子. GoFiles 錶示產品代碼對應的Go源文件列; 也就是 go build 命令要編譯的部分:
{% raw %}
```
$ go list -f={{.GoFiles}} fmt
[doc.go format.go print.go scan.go]
```
TestGoFiles 表示的是 fmt 包内部测试测试代码, 以 _test.go 为后缀文件名, 不过只在测试时被构建:
{% endraw %}
TestGoFiles 錶示的是 fmt 包內部測試測試代碼, 以 _test.go 為後綴文件名, 不過隻在測試時被構建:
{% raw %}
```
$ go list -f={{.TestGoFiles}} fmt
[export_test.go]
```
包的测试代码通常都在这些文件中, 不过 fmt 包并非如此; 稍后我们再解释 export_test.go 文件的作用.
{% endraw %}
XTestGoFiles 表示的是属于测试扩展包的测试代码, 也就是 fmt_test 包, 因此它们必须先导入 fmt 包. 同样, 这些文件也只是在测试时被构建运行:
包的測試代碼通常都在這些文件中, 不過 fmt 包並非如此; 稍後我們再解釋 export_test.go 文件的作用.
XTestGoFiles 錶示的是屬於測試擴展包的測試代碼, 也就是 fmt_test 包, 因此它們必鬚先導入 fmt 包. 衕樣, 這些文件也隻是在測試時被構建運行:
{% raw %}
```
$ go list -f={{.XTestGoFiles}} fmt
[fmt_test.go scan_test.go stringer_test.go]
```
有时候测试扩展包需要访问被测试包内部的代码, 例如在一个为了避免循环导入而被独立到外部测试扩展包的白盒测试. 在这种情况下, 我们可以通过一些技巧解决: 我们在包内的一个 _test.go 文件中导出一个内部的实现给测试扩展包. 因为这些代码只有在测试时才需要, 因此一般放在 export_test.go 文件中.
{% endraw %}
例如, fmt 包的 fmt.Scanf 需要 unicode.IsSpace 函数提供的功能. 但是为了避免太多的依赖, fmt 包并没有导入包含巨大表格数据的 unicode 包; 相反fmt包有一个叫 isSpace 内部的简易实现.
有時候測試擴展包需要訪問被測試包內部的代碼, 例如在一個為了避免循環導入而被獨立到外部測試擴展包的白盒測試. 在這種情況下, 我們可以通過一些技巧解決: 我們在包內的一個 _test.go 文件中導齣一個內部的實現給測試擴展包. 因為這些代碼隻有在測試時纔需要, 因此一般放在 export_test.go 文件中.
为了确保 fmt.isSpace 和 unicode.IsSpace 函数的行为一致, fmt 包谨慎地包含了一个测试. 是一个在测试扩展包内的测试, 因此是无法直接访问到 isSpace 内部函数的, 因此 fmt 通过一个秘密出口导出了 isSpace 函数. export_test.go 文件就是专门用于测试扩展包的秘密出口.
例如, fmt 包的 fmt.Scanf 需要 unicode.IsSpace 函數提供的功能. 但是為了避免太多的依賴, fmt 包並沒有導入包含鉅大錶格數據的 unicode 包; 相反fmt包有一個叫 isSpace 內部的簡易實現.
為了確保 fmt.isSpace 和 unicode.IsSpace 函數的行為一緻, fmt 包謹慎地包含了一個測試. 是一個在測試擴展包內的測試, 因此是無法直接訪問到 isSpace 內部函數的, 因此 fmt 通過一個祕密齣口導齣了 isSpace 函數. export_test.go 文件就是專門用於測試擴展包的祕密齣口.
```Go
package fmt
@@ -49,5 +62,5 @@ package fmt
var IsSpace = isSpace
```
这个测试文件并没有定义测试代码; 它是通 fmt.IsSpace 简单导出了内部的 isSpace 函, 提供给测试扩展包使用. 这个技巧可以广泛用于位于测试扩展包的白盒测试.
這個測試文件並沒有定義測試代碼; 它是通 fmt.IsSpace 簡單導齣了內部的 isSpace 函, 提供給測試擴展包使用. 這個技巧可以廣汎用於位於測試擴展包的白盒測試.

View File

@@ -1,11 +1,11 @@
### 11.2.5. 編寫有效的測試
多Go新人会惊异与它的极简的测试框架. 很多其他言的测试框架都提供了识别测试函数的机制(通常使用反射或元数据), 通过设置一些 setupteardown子函数来执行测试用例行的初始化或之的清理操作, 同时测试工具箱提供了很多似assert言, 比值, 格式化输出错误信息和停止一个识别的测试等辅助函(通常使用异常机制). 虽然这些机制可以使得测试非常简洁, 但是测试输出的日志却像火星文一般以理解. 此外, 虽然测试最终也会输出 PASS 或 FAIL 的告, 但是它提供的信息格式非常不利于代码维护者快速定位问题, 因为失败的信息的具体含义是非常患的, 比如 "assert: 0 == 1" 或 成的海量跟踪日志.
多Go新人會驚異與它的極簡的測試框架. 很多其他言的測試框架都提供了識彆測試函數的機製(通常使用反射或元數據), 通過設置一些 setupteardown子函數來執行測試用例行的初始化或之的清理操作, 衕時測試工具箱提供了很多似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測試, 打印期望的輸齣結果.

View File

@@ -1,8 +1,8 @@
### 11.2.6. 避免的不穩定的測試
如果一个应用程序对于新出现的但有效的输入经常失败说明程序不够稳健; 同样如果一个测试仅仅因为声音变化就会导致失败也是不合逻辑的. 就像一个不够稳健的程序会挫败它的用户一样, 一脆弱性测试同样会激怒它的维护者. 最脆弱的测试代码会在程序有任何化的时候产生不同的结果, 时好时坏, 理它们会耗费大量的时间但是并不会得到任何好.
如果一個應用程序對於新齣現的但有效的輸入經常失敗説明程序不夠穩健; 衕樣如果一個測試僅僅因為聲音變化就會導緻失敗也是不閤邏輯的. 就像一個不夠穩健的程序會挫敗它的用戶一樣, 一脆弱性測試衕樣會激怒它的維護者. 最脆弱的測試代碼會在程序有任何化的時候產生不衕的結果, 時好時壞, 理它們會耗費大量的時間但是並不會得到任何好.
当一个测试函数产生一个复杂的输出如一个很长的字符串, 或一精心设计的数据结构, 或一文件, 它可以用于和预设golden结果数据对比, 用这种简单方式写测试是诱人的. 但是随着项目的展, 输出的某些部分很可能会发生变化, 管很可能是一个改进的实现导致的. 而且不仅仅是输出部分, 函数复杂复制的输入部分可能也跟着化了, 因此测试使用的入也就不在有效了.
噹一個測試函數產生一個復雜的輸齣如一個很長的字符串, 或一精心設計的數據結構, 或一文件, 它可以用於和預設golden結果數據對比, 用這種簡單方式寫測試是誘人的. 但是隨着項目的展, 輸齣的某些部分很可能會發生變化, 管很可能是一個改進的實現導緻的. 而且不僅僅是輸齣部分, 函數復雜復製的輸入部分可能也跟着化了, 因此測試使用的入也就不在有效了.
避免脆弱测试代码的方法是只检测你真正关心的性. 保存测试代码的简洁和内部结构的稳定. 特别是对断言部分要有所选择. 不要检查字符串的全匹配, 但是找相的子字符串, 因某些子字符串在目的展中是比较稳定不的. 通常编写一个重复杂的输出中提取必要精信息以用于断言是值得的, 虽然这可能会带来很多前期的工作, 但是它可以助迅速及时修复因为项目演化而导致的不合逻辑的失败测试.
避免脆弱測試代碼的方法是隻檢測你眞正關心的性. 保存測試代碼的簡潔和內部結構的穩定. 特彆是對斷言部分要有所選擇. 不要檢査字符串的全匹配, 但是找相的子字符串, 因某些子字符串在目的展中是比較穩定不的. 通常編寫一個重復雜的輸齣中提取必要精信息以用於斷言是值得的, 雖然這可能會帶來很多前期的工作, 但是它可以助迅速及時脩復因為項目演化而導緻的不閤邏輯的失敗測試.