先看一個需求
在咱們工做中,咱們會遇到這樣的狀況,就是去確認一個函數,或者一個模塊的結果是否正確.
傳統的方法
15.2.1 傳統的方式來進行測試
在 main 函數中,調用 addUpper 函數,看看實際輸出的結果是否和預期的結果一致,若是一致,
則說明函數正確,不然函數有錯誤,而後修改錯誤json
1) 不方便, 咱們須要在 main 函數中去調用,這樣就須要去修改 main 函數,若是如今項目正在運
行,就可能去中止項目。
2) 不利於管理,由於當咱們測試多個函數或者多個模塊時,都須要寫在 main 函數,不利於咱們管
理和清晰咱們思路
3) 引出單元測試。-> testing 測試框架 能夠很好解決問題。併發
Go 語言中自帶有一個輕量級的測試框架 testing 和自帶的 go test 命令來實現單元測試和性能測試,
testing 框架和其餘語言中的測試框架相似,能夠基於這個框架寫針對相應函數的測試用例,也能夠基
於該框架寫相應的壓力測試用例。經過單元測試,能夠解決以下問題
1) 確保 每一個函數是可運行,而且運行結果是正確的
2) 確保寫出來的代碼 性能是好的,
3) 單元測試能及時的發現程序設計或實現的 邏輯錯誤,使問題及早暴露,便於問題的定位解決,
而 性能測試的重點在於發現程序設計上的一些問題,讓程序可以在高併發的狀況下還能保持穩定框架
使用 Go 的單元測試,對 addUpper 和 sub 函數進行測試。
特別說明: 測試時,可能須要暫時退出 360。(由於 360 可能會認爲生成的測試用例程序是木馬)函數
單元測試快速入門總結
1) 測試用例文件名必須以 _test.go 結尾。 好比 cal_test.go , cal 不是固定的。
2) 測試用例函數必須以 Test 開頭,通常來講就是 Test+被測試的函數名,好比 TestAddUpper
3) TestAddUpper(t tesing.T) 的形參類型必須是 testing.T 【看一下手冊】
4) 一個測試用例文件中,能夠有多個測試用例函數,好比 TestAddUpper、TestSub
5) 運行測試用例指令高併發
(1) cmd>go test [若是運行正確,無日誌,錯誤時,會輸出日誌]性能
(2) cmd>go test -v [運行正確或是錯誤,都輸出日誌]
6) 當出現錯誤時,可使用 t.Fatalf 來格式化輸出錯誤信息,並退出程序
7) t.Logf 方法能夠輸出相應的日誌
8) 測試用例函數,並無放在 main 函數中,也執行了,這就是測試用例的方便之處.
9) PASS 表示測試用例運行成功,FAIL 表示測試用例運行失敗
10) 測試單個文件,必定要帶上被測試的原文件
go test -v cal_test.go cal.go
11) 測試單個方法
go test -v -test.run TestAddUpper單元測試
main包中:main.go測試
package main import ( _ "fmt" ) //一個被測試函數 func addUpper(n int) int { res := 0 for i := 1; i <= n - 1; i++ { res += i } return res } func addUpper2(n int) int { res := 0 for i := 1; i <= n - 1; i++ { res += i } return res } func main() { //傳統的測試方法,就是在main函數中使用看看結果是否正確 // res := addUpper(10) // 1.+ 10 = 55 // if res != 55 { // fmt.Printf("addUpper錯誤 返回值=%v 指望值=%v\n", res, 55) // } else { // fmt.Printf("addUpper正確 返回值=%v 指望值=%v\n", res, 55) // } }
text包中:cal_test.gothis
package cal import ( "fmt" "testing" //引入go 的testing框架包 ) //編寫要給測試用例,去測試addUpper是否正確 func TestAddUpper(t *testing.T) { //調用 res := addUpper(10) if res != 55 { //fmt.Printf("AddUpper(10) 執行錯誤,指望值=%v 實際值=%v\n", 55, res) t.Fatalf("AddUpper(10) 執行錯誤,指望值=%v 實際值=%v\n", 55, res) } //若是正確,輸出日誌 t.Logf("AddUpper(10) 執行正確...") } func TestHello(t *testing.T) { fmt.Println("TestHello被調用..") }
text包中:sub_test.go設計
package cal import ( _ "fmt" "testing" //引入go 的testing框架包 ) //編寫要給測試用例,去測試addUpper是否正確 func TestGetSub(t *testing.T) { //調用 res := getSub(10, 3) if res != 7 { //fmt.Printf("AddUpper(10) 執行錯誤,指望值=%v 實際值=%v\n", 55, res) t.Fatalf("getSub(10, 3) 執行錯誤,指望值=%v 實際值=%v\n", 7, res) } //若是正確,輸出日誌 t.Logf("getSub(10, 3) 執行正確!!!!...") }
text包中:cal.go
package cal //一個被測試函數 func addUpper(n int) int { res := 0 for i := 1; i <= n - 1; i++ { res += i } return res } //求兩個數的查 func getSub(n1 int, n2 int) int { return n1 - n2 }
如下兩個文件處在一個包中:
monster.go:
package monster import ( "encoding/json" "io/ioutil" "fmt" ) 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 := "d:/monster.ser" err = ioutil.WriteFile(filePath, data, 0666) if err != nil { fmt.Println("write file err =", err) return false } return true } //給Monster綁定方法ReStore, 能夠將一個序列化的Monster,從文件中讀取, //並反序列化爲Monster對象,檢查反序列化,名字正確 func (this *Monster) ReStore() bool { //1. 先從文件中,讀取序列化的字符串 filePath := "d:/monster.ser" data, err := ioutil.ReadFile(filePath) if err != nil { fmt.Println("ReadFile err =", err) return false } //2.使用讀取到data []byte ,對反序列化 err = json.Unmarshal(data, this) if err != nil { fmt.Println("UnMarshal err =", err) return false } return true }
monster_test.go:
package monster import ( "testing" ) //測試用例,測試 Store 方法 func TestStore(t *testing.T) { //先建立一個Monster 實例 monster := &Monster{ Name : "紅孩兒", Age :10, Skill : "吐火.", } res := monster.Store() if !res { t.Fatalf("monster.Store() 錯誤,但願爲=%v 實際爲=%v", true, res) } t.Logf("monster.Store() 測試成功!") } func TestReStore(t *testing.T) { //測試數據是不少,測試不少次,才肯定函數,模塊.. //先建立一個 Monster 實例 , 不須要指定字段的值 var monster = &Monster{} res := monster.ReStore() if !res { t.Fatalf("monster.ReStore() 錯誤,但願爲=%v 實際爲=%v", true, res) } //進一步判斷 if monster.Name != "紅孩兒" { t.Fatalf("monster.ReStore() 錯誤,但願爲=%v 實際爲=%v", "紅孩兒", monster.Name) } t.Logf("monster.ReStore() 測試成功!") }