單元測試 先看一個需求 在我們工作中,我們會遇到這樣的情況,就是去確認一個函數,或者一個模塊的結果是否正確. 傳統的方法 15.2.1 傳統的方式來進行測試 在 main 函數中,調用 addUpper 函數,看看實際輸出的結果是否和預期的結果一致,如果一致, 則說明函數正確,否則函數有錯誤,然後修 ...
單元測試
先看一個需求
在我們工作中,我們會遇到這樣的情況,就是去確認一個函數,或者一個模塊的結果是否正確.
傳統的方法
15.2.1 傳統的方式來進行測試
在 main 函數中,調用 addUpper 函數,看看實際輸出的結果是否和預期的結果一致,如果一致,
則說明函數正確,否則函數有錯誤,然後修改錯誤
傳統方法的缺點分析
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.go
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() 測試成功!")
}