生活随笔
收集整理的這篇文章主要介紹了
《Go语言圣经》学习笔记 第十一章 测试
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
《Go語言圣經》學習筆記 第十一章 測試
目錄
go test 測試函數 測試覆蓋率 基準測試 剖析 示例函數
注:學習《Go語言圣經》筆記,PDF點擊下載,建議看書。 Go語言小白學習筆記,書上的內容照搬,大佬看了勿噴,以后熟悉了會總結成自己的讀書筆記。
Maurice Wilkes, 第一個存儲程序計算機EDSAC的設計者, 1949年他在實驗室爬樓梯時有一個頓悟。 在《計算機先驅回憶錄》 ( Memoirs of a Computer Pioneer) 里, 他回憶到: “忽然間有一種醍醐灌頂的感覺, 我整個后半生的美好時光都將在尋找程序BUG中度過了”。 肯定從那之后的大部分正常的碼農都會同情Wilkes過份悲觀的想法, 雖然也許不是沒有人困惑于他對軟件開發的難度的天真看法。 現在的程序已經遠比Wilkes時代的更大也更復雜, 也有許多技術可以讓軟件的復雜性可得到 控制。 其中有兩種技術在實踐中證明是比較有效的。 第一種是代碼在被正式部署前需要進行 代碼評審。 第二種則是測試, 也就是本章的討論主題。 我們說測試的時候一般是指自動化測試, 也就是寫一些小的程序用來檢測被測試代碼( 產品 代碼) 的行為和預期的一樣, 這些通常都是精心設計的執行某些特定的功能或者是通過隨機 性的輸入要驗證邊界的處理。 軟件測試是一個巨大的領域。 測試的任務可能已經占據了一些程序員的部分時間和另一些程序員的全部時間。 和軟件測試技術相關的圖書或博客文章有成千上萬之多。 對于每一種主流的編程語言, 都會有一打的用于測試的軟件包, 同時也有大量的測試相關的理論, 而且每種都吸引了大量技術先驅和追隨者。 這些都足以說服那些想要編寫有效測試的程序員重新學習一套全新的技能。 Go語言的測試技術是相對低級的。 它依賴一個go test測試命令和一組按照約定方式編寫的測試函數, 測試命令可以運行這些測試函數。 編寫相對輕量級的純測試代碼是有效的, 而且它很容易延伸到基準測試和示例文檔。 在實踐中, 編寫測試代碼和編寫程序本身并沒有多大區別。 我們編寫的每一個函數也是針對每個具體的任務。 我們必須小心處理邊界條件, 思考合適的數據結構, 推斷合適的輸入應該產生什么樣的結果輸出。 編程測試代碼和編寫普通的Go代碼過程是類似的; 它并不需要學習新的符號、 規則和工具。
1. go test
go test命令是一個按照一定的約定和組織的測試代碼的驅動程序。 在包目錄內, 所有以_test.go為后綴名的源文件并不是go build構建包的一部分, 它們是go test測試的一部分。 在*_test.go文件中, 有三種類型的函數: 測試函數、 基準測試函數、 示例函數。 一個測試函數是以Test為函數名前綴的函數, 用于測試程序的一些邏輯行為是否正確; go test命令會調用這些測試函數并報告測試結果是PASS或FAIL。 基準測試函數是以Benchmark為函數名前綴的函數, 它們用于衡量一些函數的性能; go test命令會多次運行基準函數以計算一個平均的執行時間。 示例函數是以Example為函數名前綴的函數, 提供一個由編譯器保證正確性的示例文 檔。 我們將在11.2節討論測試函數的所有細節, 病在11.4節討論基準測試函數的細節, 然后11.6節討論示例函數的細節。 go test命令會遍歷所有的*_test.go文件中符合上述命名規則的函數, 然后生成一個臨時的main包用于調用相應的測試函數, 然后構建并運行、 報告測試結果, 最后清理測試中生成的臨時文件
2. 測試函數
每個測試函數必須導入testing包。 測試函數有如下的簽名: 測試函數的名字必須以Test開頭, 可選的后綴名必須以大寫字母開頭: 其中t參數用于報告測試失敗和附加的日志信息。 讓我們定義一個實例包gopl.io/ch11/word1,其中只有一個函數IsPalindrome用于檢查一個字符串是否從前向后和從后向前讀都是一樣的。( 下面這個實現對于一個字符串是否是回文字符串前后重復測試了兩次; 我們稍后會再討論這個問題。 ) gopl.io/ch11/word1 在相同的目錄下, word_test.go測試文件中包含了TestPalindrome和TestNonPalindrome兩個測試函數。 每一個都是測試IsPalindrome是否給出正確的結果, 并使用t.Error報告失敗信息: go test 命令如果沒有參數指定包那么將默認采用當前目錄對應的包( 和 go build 命令一樣) 。 我們可以用下面的命令構建和運行測試。 結果還比較滿意, 我們運行了這個程序, 不過沒有提前退出是因為還沒有遇到BUG報告。 不過一個法國名為“Noelle Eve Elleon”的用戶會抱怨IsPalindrome函數不能識別“été”。 另外一個來自美國中部用戶的抱怨則是不能識別“A man, a plan, a canal: Panama.”。 執行特殊和小的BUG報告為我們提供了新的更自然的測試用例。 為了避免兩次輸入較長的字符串, 我們使用了提供了有類似Printf格式化功能的 Errorf函數來匯報錯誤結果。 當添加了這兩個測試用例之后, go test 返回了測試失敗的信息 先編寫測試用例并觀察到測試用例觸發了和用戶報告的錯誤相同的描述是一個好的測試習慣。 只有這樣, 我們才能定位我們要真正解決的問題。 先寫測試用例的另外的好處是, 運行測試通常會比手工描述報告的處理更快, 這讓我們可以進行快速地迭代。 如果測試集有很多運行緩慢的測試, 我們可以通過只選擇運行某些特定的測試來加快測試速度。 參數 -v 可用于打印每個測試函數的名字和運行時間: 參數 -run 對應一個正則表達式, 只有測試函數名被它正確匹配的測試函數才會被 go test 測試命令運行: 當然, 一旦我們已經修復了失敗的測試用例, 在我們提交代碼更新之前, 我們應該以不帶參數的 go test 命令運行全部的測試用例, 以確保修復失敗測試的同時沒有引入新的問題。 我們現在的任務就是修復這些錯誤。 簡要分析后發現第一個BUG的原因是我們采用了 byte而不是rune序列, 所以像“été”中的é等非ASCII字符不能正確處理。 第二個BUG是因為沒有忽略空格和字母的大小寫導致的。 針對上述兩個BUG, 我們仔細重寫了函數: gopl.io/ch11/word2 同時我們也將之前的所有測試數據合并到了一個測試中的表格中。 現在我們的新測試阿都通過了: 這種表格驅動的測試在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的長度, 輸出一個相關部分的簡明總結即可。 測試的作者應該要努力幫助程序員診斷測試失敗的原因。
1. 隨機測試
表格驅動的測試便于構造基于精心挑選的測試數據的測試用例。 另一種測試思路是隨機測試, 也就是通過構造更廣泛的隨機輸入來測試探索函數的行為。 那么對于一個隨機的輸入, 我們如何能知道希望的輸出結果呢? 這里有兩種處理策略。 第一個是編寫另一個對照函數, 使用簡單和清晰的算法, 雖然效率較低但是行為和要測試的函數是一致的, 然后針對相同的隨機輸入檢查兩者的輸出結果。 第二種是生成的隨機輸入的數據遵循特定的模式, 這樣我們就可以知道期望的輸出的模式。 下面的例子使用的是第二種方法: randomPalindrome函數用于隨機生成回文字符串。 雖然隨機測試會有不確定因素, 但是它也是至關重要的, 我們可以從失敗測試的日志獲取足夠的信息。 在我們的例子中, 輸入IsPalindrome的p參數將告訴我們真實的數據, 但是對于函數將接受更復雜的輸入, 不需要保存所有的輸入, 只要日志中簡單地記錄隨機數種子即可 ( 像上面的方式) 。 有了這些隨機數初始化種子, 我們可以很容易修改測試代碼以重現失敗的隨機測試。 通過使用當前時間作為隨機種子, 在整個過程中的每次運行測試命令時都將探索新的隨機數據。 如果你使用的是定期運行的自動化測試集成系統, 隨機測試將特別有價值
2. 測試一個命令
對于測試包 go test 是一個的有用的工具, 但是稍加努力我們也可以用它來測試可執行程序。 如果一個包的名字是 main, 那么在構建時會生成一個可執行程序, 不過main包可以作為一個包被測試器代碼導入。 讓我們為2.3.2節的echo程序編寫一個測試。 我們先將程序拆分為兩個函數: echo函數完成真正的工作, main函數用于處理命令行輸入參數和echo可能返回的錯誤。 gopl.io/ch11/echo 在測試中我們可以用各種參數和標標志調用echo函數, 然后檢測它的輸出是否正確, 我們通過增加參數來減少echo函數對全局變量的依賴。 我們還增加了一個全局名為out的變量來替代直接使用os.Stdout, 這樣測試代碼可以根據需要將out修改為不同的對象以便于檢查。 下面就是echo_test.go文件中的測試代碼: 要注意的是測試代碼和產品代碼在同一個包。 雖然是main包, 也有對應的main入口函數, 但是在測試的時候main包只是TestEcho測試函數導入的一個普通包, 里面main函數并沒有被導出, 而是被忽略的。 通過將測試放到表格中, 我們很容易添加新的測試用例。 讓我通過增加下面的測試用例來看看失敗的情況是怎么樣的: go test 輸出如下: 錯誤信息描述了嘗試的操作( 使用Go類似語法) , 實際的結果和期望的結果。 通過這樣的錯誤信息, 你可以在檢視代碼之前就很容易定位錯誤的原因。 要注意的是在測試代碼中并沒有調用log.Fatal或os.Exit, 因為調用這類函數會導致程序提前退出; 調用這些函數的特權應該放在main函數中。 如果真的有意外的事情導致函數發生panic異常, 測試驅動應該嘗試用recover捕獲異常, 然后將當前測試當作失敗處理。 如果是可預期的錯誤, 例如非法的用戶輸入、 找不到文件或配置文件不當等應該通過返回一個非空的error的方式處理。 幸運的是( 上面的意外只是一個插曲) , 我們的echo示例是比較簡單的也沒有需要返回非空error的情況。
3. 白盒測試
一種測試分類的方法是基于測試者是否需要了解被測試對象的內部工作原理。 黑盒測試只需要測試包公開的文檔和API行為, 內部實現對測試代碼是透明的。 相反, 白盒測試有訪問包內部函數和數據結構的權限, 因此可以做到一下普通客戶端無法實現的測試。 例如, 一個白盒測試可以在每個操作之后檢測不變量的數據類型。 ( 白盒測試只是一個傳統的名稱, 其實稱為clear box測試會更準確。 ) 黑盒和白盒這兩種測試方法是互補的。 黑盒測試一般更健壯, 隨著軟件實現的完善測試代碼很少需要更新。 它們可以幫助測試者了解真是客戶的需求, 也可以幫助發現API設計的一些不足之處。 相反, 白盒測試則可以對內部一些棘手的實現提供更多的測試覆蓋。 我們已經看到兩種測試的例子。 TestIsPalindrome測試僅僅使用導出的IsPalindrome函數, 因此這是一個黑盒測試。 TestEcho測試則調用了內部的echo函數, 并且更新了內部的out包級變量, 這兩個都是未導出的, 因此這是白盒測試。 當我們準備TestEcho測試的時候, 我們修改了echo函數使用包級的out變量作為輸出對象, 因此測試代碼可以用另一個實現代替標準輸出, 這樣可以方便對比echo輸出的數據。 使用類似的技術, 我們可以將產品代碼的其他部分也替換為一個容易測試的偽對象。 使用偽對象的好處是我們可以方便配置, 容易預測, 更可靠, 也更容易觀察。 同時也可以避免一些不良的副作用, 例如更新生產數據庫或信用卡消費行為。 下面的代碼演示了為用戶提供網絡存儲的web服務中的配額檢測邏輯。 當用戶使用了超過90%的存儲配額之后將發送提醒郵件。 gopl.io/ch11/storage1 我們想測試這個代碼, 但是我們并不希望發送真實的郵件。 因此我們將郵件處理邏輯放到一個私有的notifyUser函數中。 gopl.io/ch11/storage2 現在我們可以在測試中用偽郵件發送函數替代真實的郵件發送函數。 它只是簡單記錄要通知的用戶和郵件的內容。 這里有一個問題: 當測試函數返回后, CheckQuota將不能正常工作, 因為notifyUsers依然使用的是測試函數的偽發送郵件函數( 當更新全局對象的時候總會有這種風險) 。 我們必須修改測試代碼恢復notifyUsers原先的狀態以便后續其他的測試沒有影響, 要確保所有的執行路徑后都能恢復, 包括測試失敗或panic異常的情形。 在這種情況下, 我們建議使用defer語句來延后執行處理恢復的代碼。 這種處理模式可以用來暫時保存和恢復所有的全局變量, 包括命令行標志參數、 調試選項和優化參數; 安裝和移除導致生產代碼產生一些調試信息的鉤子函數; 還有有些誘導生產代碼進入某些重要狀態的改變, 比如超時、 錯誤, 甚至是一些刻意制造的并發行為等因素。 以這種方式使用全局變量是安全的, 因為go test命令并不會同時并發地執行多個測試。
待續…
總結
以上是生活随笔 為你收集整理的《Go语言圣经》学习笔记 第十一章 测试 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。