十五、單元測試

zhangsen發表於2021-04-20

開發中需要確認一個函式、模組結果是否正確

func addUpper(n int)int{
   res:=0
   for i := 1; i <n ; i++ {
      res+=i
   }
   return res
}

傳統方法測試就是呼叫這個函式,看返回結果是否正確

缺點:

  1. 需要在main函式中呼叫,測試的時候去修改main函式,執行中的專案得停止來測試
  2. 不利於管理,因為當我們測試多個函式或者多個模組時,都要寫在main函式中,邏輯混亂
  3. 引出黨員測試,testing測試框架,可以很好解決問題

基本介紹

Go 語言中自帶有一個輕量級的測試框架 testing 和自帶的 go test 命令來實現單元測試和效能測試,testing 框架可以寫測試用例、壓力測試用例,

單元測試作用 :

  1. 確保每個函式是可執行,並且執行結果是正確的
  2. 確保寫出來的程式碼效能是好的,
  3. 單元測試能及時的發現程式設計或實現的邏輯錯誤,使問題及早暴露,便於問題的定位解決, 而效能測試的重點在於發現程式設計上的一些問題,讓程式能夠在高併發的情況下還能保持穩定

單元測試

cal.go

func AddUpper(n int)int{
   res:=0
   for i := 0; i < n; i++ {
      res += i
   }
   return res
}

cal_test.go

//編寫測試用例,測試addUpper是否正常
func TestAddUpper(t *testing.T) {
   res := AddUpper(10)
   if res != 55 {
      fmt.Println("AddUpper(10)執行錯誤,期望值=%v 實際值=%v", 55, res)
   }

   //輸出日誌
   t.Logf("AddUpper(10)執行正確")
}
C:\Users\55480\go\go\learn\testcase01>go test -v 
# learngo/learn/testcase01 
.\cat_test.go:15:3: Println call has possible formatting directive %v 
FAIL    learngo/learn/testcase01 [build failed] 

# learngo/learn/testcase01 
.\cat_test.go:15:3: Println call has possible formatting directive %v 
FAIL    learngo/learn/testcase01 [build failed] 

C:\Users\55480\go\go\learn\testcase01>go test -v 
=== RUN   TestAddUpper 
AddUpper(10)執行錯誤,期望值=55 實際值=45    cat_test.go:18: AddUpper(10)執行正確 
--- PASS: TestAddUpper (0.00s) 
PASS 
ok      learngo/learn/testcase01        0.080s 

testing框架,會將xxx_test.go的檔案加入,呼叫testXxx方法

總結

  1. 測試用例檔名必須以_test.go結尾
  2. 測試用例函式必須以Test開頭(Test+函式名)
  3. 形參必須是(T *tesing.T)
  4. 一個測試用例檔案中,可以有多個測試用例函式,也可與在不同檔案,只要命名統一
  5. 執行測試用例指令
    1. go test 執行正確,無日誌,錯誤會輸出錯誤日誌
    2. go test -v 正確錯誤都會輸出日誌
  6. 當出現錯誤時,可以使用 t.Fatalf 來格式化輸出錯誤資訊,並退出程式
  7. t.Logf 方法可以輸出相應的日誌
  8. 測試單個檔案,一定要帶上被測試的原檔案 :C:\Users\55480\go\learn\testcase01>go test -v cat_test.go cal.go
  9. 測試單個方法 : go test -v -test.run TestAddUpper
  10. 預設掃描當前資料夾內所有的測試用例

案例:

package main

import (
   "encoding/json"
   "fmt"
   "io/ioutil"
)

type monster struct {
   Name string
   Age int
   Skill string
}
//給monster繫結方法store,可以將一個monster變數(物件),序列化後儲存到檔案中
func (this *monster) Store()bool{
   //先序列化
   data,err:=json.Marshal(this)
   if err!=nil{
      fmt.Println("marshal err=",err)
      return  false
   }

   //儲存到檔案
   filePath:="c/monster.ser"
   err=ioutil.WriteFile(filePath,data,0777)
   if err!=nil{
      fmt.Println("write fiule err=",err)
      return false
   }
   return true
}

func (this *monster) ReStore()bool{
   //反先序列化
   filePath:="d/monster.ser"
   data,err:=ioutil.ReadFile(filePath)
   if err!=nil{
      fmt.Println("readFile ",err)
      return false
   }

   //反序列化
   err= json.Unmarshal(data,this)
   if err!=nil{
      fmt.Println("Unmarshal err ",err)
      return false
   }
   return true
}

測試案例

package main

import "testing"

func TestStore(t *testing.T){
   //先建立一個monster例項
   monster := &monster{
      Name:"擦拭",
      Age:12,
      Skill:"風火輪",
   }
   res := monster.Store()
   if res{
      t.Fatalf("方法錯誤,希望為%v,實際為%v",true,res)
   }
   t.Logf("測試成功")
}

func TestReStore(t *testing.T){
   //先建立一個monster例項
   var monster= &monster{}
   res:=monster.ReStore()
   if res{
      t.Fatalf("方法錯誤,希望為%v,實際為%v",true,res)
   }

   if monster.Name !="擦拭" {
      t.Fatalf("方法錯誤,希望為%v,實際為%v","擦拭",res)

   }
   t.Logf("測試成功")
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章