Go語言工程實踐之測試 | 青訓營筆記

語言: CN / TW / HK

theme: nico

這是我參與「第五屆青訓營 」伴學筆記創作活動的第 4 天

前言

記錄加入青訓營的每一天的日筆記

背景

測試的出現是為了避免專案中出現重大事故

測試是避免事故的最後一道屏障

測試

image.png

單元測試的覆蓋率在一定程度上而言,決定了程式碼的質量

單元測試

image.png

通過測試單元的輸出與期望值進行校對從而驗證程式碼的正確性,從而保證新舊程式碼的互不影響與程式的正常執行。

進而單元測試較於編譯更易於在較短的週期內發現和定位程式碼中的錯誤使損失最小化從而提升效率。所以寫單元測試是很有必要的。

Golang單元測試對檔名和方法名,引數都有很嚴格的要求

  • 檔名必須以xx_test.go命名
  • 方法必須是Test[^a-z]開頭
  • 方法引數必須t *testing.T
  • 初始化邏輯放到TestMain中
  • 使用go test執行單元測試

演示

通過第三方包assert演示單元測試

判斷函式測試值與期望值是否一致

import( "github.com/stretchr/testify/assert" "testing" ) ​ func TestHelloTom(t *testing.T) {    output := HelloTom()    expectOutput := "Tom"    assert.Equal(t, expectOutput, output) } ​ func HelloTom() string {    return "Tom" }

覆蓋率

覆蓋率出現的目的:

  • 衡量程式碼是否經過了足夠的測試
  • 評價專案的測試水準
  • 評估專案是否達到了高水準測試等級

通過go test命令測試函式的覆蓋率

// judgment.go func JudgePassLine(score int16) bool {    if score >= 60 {        return true   }    else {        return false   } } ​ // judgment_test.go func TestJudgePassLineTrue(t *testing.T) {    isPass := JudgeePassLine(70)    assert.Equal(t, true, isPass) } ​ func TestJudgePassLineFalse(t *testing.T) {    isPass := JudgeePassLine(50)    assert.Equal(t, false, isPass) } ​ /* 通過go test 命令測試覆蓋率 go test judgment_test.go judgment.go --cover */

  • 一般覆蓋率:50%~60%,較高覆蓋率:80%+

  • 測試分支相互獨立、全面覆蓋

    對於上述案例程式碼而言

    應出現成績大於等於60 和小於60的測試用力

  • 測試單元粒度足夠小,函式單一職責

依賴

image.png

  • 冪等:重複運行同一個case,結果與之前一致
  • 穩定:指單元測試相互隔離,可以獨立執行

檔案處理

當測試檔案被修改後,可能會導致測試失敗或錯誤率增高

從而出現了Mock函式

func ReadFirstLine() string { open, err := os.Open("log") // 開啟一個檔案 defer open.Close() if err != nil { return "" } scanner := bufio.NewScanner(open) // 對每行進行遍歷 for scanner.Scan() { return scanner.Text() } return "" } ​ func ProcessFirstLine() string { line := ReadFirstLine() destLine := strings.ReplaceAll(line, "11", "00") // 替換11為00 return destLine } ​ func TestProcessFirstLine(t *testing.T) { // 執行單元測試 firstLine := ProcessFirstLine() assert.Equal(t, "line00", firstLine) }

Mock

monkey: https://github.com/bouk/monkey 這是一個開源的mock測試庫,可以對method或者例項的方法進行mock

Monkey Patch的作用域在Runtime, 執行時通過Go的unsafe包能夠將記憶體中函式的地址替換為執行時函式的地址,將待打樁函式或方法的實現跳轉。

Mock函式不僅可以為一個函式打樁 也可以為一個方法打樁

// 用函式A去替換函式B,B就是原函式,A就是打樁函式 ​ func Patch(target, replacement interface{}) *PatchGuard {    // target就是原函式,replacement就是打樁函式 t := reflect.ValueOf(target) r := reflect.ValueOf(replacement) patchValue(t, r) return &PatchGuard{t, r} } ​ func Unpatch(target interface{}) bool {    // 保證了在測試結束之後需要把這個包解除安裝掉 return unpatchValue(reflect.ValueOf(target)) } ​ func TestProcessFirstLineWithMock(t *testing.T) { monkey.Patch(ReadFirstLine, func() string { return "line110" }) defer monkey.Unpatch(ReadFirstLine) line := ProcessFirstLine() assert.Equal(t, "line000", line) } // 通過patch對ReadFirstLine進行打樁mock,預設返回line110,通過defer解除安裝mock // 這樣整個測試函式就擺脫了本地檔案的束縛和依賴

基準測試

基準測試是指測試一段程式的效能及耗費CPU的程度;

在實際的專案開發中,經常會遇到程式碼效能瓶頸,為了定位問題,經常要對程式碼做效能分;

這時就用到了基準測試,其使用方法與單元測試類似。

  • 優化程式碼,需要對當前程式碼分析
  • 內建的測試框架提供了基準測試的能力

小結

對於今日課程而言,我將其劃分成測試的重要性與分類。 當前課程餘下部分為專案實戰,該部分內容選擇了放置於專案筆記。 如果筆記中有錯誤的地方也希望掘友們可以及時的提出糾正。