Go單元測試實踐


  單元測試通常用來在日常開發中檢查代碼中存在的問題,是提升代碼質量一種有效手段。在保證代碼功能沒有問題的同時,可以得到預期結果。Golang有許多優秀的框架支持UT,下面列舉日常開發中不同框架對應的UT情況,以便后來人實踐UT。

  1、Goland提供的簡單UT模板

  用途:對其中一個函數、方法生成UT

  介紹:在新手不知道如何寫UT,按照什么規范去編寫UT的時候,不妨采用Goland自帶的模板。

  tips:在Goland選中要測試的方法、函數--“generate”--"Test for selection",然后在// TODO: Add test cases中加入自己的測試用例數據即可。

  缺點:通用性一般,適合邏輯簡單的函數。

      2、Convey

  用途:較為好用的UT框架,斷言多樣,提供UT的web界面,較為常用

  官方文檔:link

  tips:Testify: 斷言庫,斷言功能豐富,簡單易用,通常和其他框架搭配使用

  code example:

  

 1 // TestFunc
 2 func CheckVal(val string) bool {
 3     valList := []string{"AAA", "BBB"}
 4     for index := range valList {
 5         if valList[index] == val {
 6             return true
 7         }
 8     }
 9     return false
10 }
11 
12 
13 // Convey特點:斷言函數豐富,提供UT的web界面,較為常用
14 // Convey website: https://github.com/smartystreets/goconvey/wiki/Documentation
15 
16 // Testify: 斷言庫,多用它提供的豐富的斷言功能
17 func TestCheckVal(t *testing.T) {
18     convey.Convey("TestCheckVal happy path", t, func(){
19         res := CheckVal("AAA")
20         convey.So(res, convey.ShouldBeTrue)
21         assert.True(t, res)
22     })
23 
24     convey.Convey("TestCheckVal unhappy path", t, func(){
25         res := CheckVal("CCC")
26         convey.So(res, convey.ShouldBeFalse)
27         assert.False(t, res)
28     })
29 }
View Code

 

 

  3、Gomonkey

  tips:

    mac電腦在用Gomonkey打樁的時候時常會遇到syscall的問題,是該框架的一個bug,需要配置插件來解決這個問題,具體可以參照國外作者的配置 link

  code example:

  

 1 // Gomonkey: 十分常用的打樁工具,是在monkey框架基礎上個人改進的一款UT框架,比較好奇公司喜歡用這類個人庫而不是monkey這種偏官方的庫寫UT
 2 // Gomonkey支持多種打樁特性,但在平常開發中多用來對全局變量打樁;對函數打樁;對方法打樁;對函數變量進行打樁
 3 
 4 // 1、為函數打樁 ApplyFunc(origin func, stub func)
 5 func CalVal(a, b int) int {
 6     return a + b
 7 }
 8 
 9 func TestCalVal(t *testing.T) {
10     convey.Convey("happy path", t, func() {
11         patch := gomonkey.ApplyFunc(CalVal, func(a, b int) int {
12             return 3
13         })
14         defer patch.Reset()
15 
16         res := CalVal(1, 2)
17         convey.So(res, convey.ShouldEqual, 3) // or use assert
18     })
19 }
20 
21 // 2、為全局變量打樁 ApplyGlobalVar(&var, stubvalue)
22 
23 var temp = 10
24 func TestGlobal(t *testing.T) {
25     convey.Convey("happy path", t, func() {
26         patch := gomonkey.ApplyGlobalVar(&temp, 15)
27         defer patch.Reset()
28         convey.So(temp, convey.ShouldEqual, 15)
29     })
30 }
31 
32 // 3、對函數變量打樁 ApplyFuncVar (&func, stubfunc)   equal to ApplyFunc, but it is the address of func var
33 
34 // 4、對方法打樁 ApplyMethod(reflect.TypeOf, methodName, stubfunc)
35 
36 type User struct {
37     money int
38 }
39 
40 func(u *User) AddMoney(temp int) int{
41     u.money += temp
42     return u.money
43 }
44 
45 func TestAddMoney(t *testing.T) {
46     temp := User{}
47     patch := gomonkey.ApplyMethod(reflect.TypeOf(&temp), "AddMoney", func(_ *User, temp int) int{
48         return 100
49     })
50     defer patch.Reset()
51 
52     convey.Convey("happy path", t, func() {
53         Bob := &User{
54             money: 20,
55         }
56         convey.So(Bob.AddMoney(80),convey.ShouldEqual, 100)
57     })
58 
59 }
View Code

 

 

  4、Gomock

  用途:對接口進行mock,提前定義好返回內容

  介紹:go官方提供的框架,同時還可以使用mockGen工具來生成接口的mock代碼

  tips:

    使用mockgen生成接口的mock代碼

    mockgen -source= A.go -destination= A_mock.go -package=main     

  code example:    

  

 1 // gomock 多用於接口打樁,結合mockgen工具自動生成打樁代碼,方便調用
 2 
 3 type Repo interface {
 4     GetName(id int) string
 5 }
 6 
 7 func TestGetName(t *testing.T) {
 8     // new mockController
 9     ctrl := gomock.NewController(t)
10     defer ctrl.Finish()
11 
12     // mock Repo interface
13     mock := mock_go_UT_learn.NewMockRepo(ctrl)
14     mock.EXPECT().GetName(gomock.Eq(1)).Return("JD_1")
15     if val := mock.GetName(1); val != "JD_1" {
16         t.Fatal("expected JD_1, but got", val)
17     } else {
18         fmt.Println("the result is ", val)
19     }
20 
21 }
22 
23 //go:generate mockgen -source=./gomock_test.go -destination=./mock/mocktest_mock.go
View Code

 

 

  5、SqlMock

  用途:模擬數據庫請求,無需關注數據庫連接

  介紹:實際遇到的情況是SqlMock和GORM的的測試

  tips: mock.ExpectQuery("content").WillReturnRows(sqlmock.NewRows([]string{"id"}))   在WillReturnRows里不addRow就可以返回空行了

  code example:

  

 1 type Goods struct {
 2     name string
 3 }
 4 type Repository struct {
 5     db *gorm.DB
 6 }
 7 
 8 
 9 func(p *Repository)ListAll()([]*Goods, error) {
10     var goods []*Goods
11     err := p.db.Find(&goods).Error
12     return goods, err
13 }
14 
15 
16 func TestSql(t *testing.T) {
17     // 使用 sqlmock.New() 創建 *sql.DB 的模擬實例和模擬控制器
18     db, mock, err := sqlmock.New()
19     if nil != err {
20         log.Fatalf("Init sqlmock failed, err %v", err)
21     }
22     // create gorm link with sqlmock
23     dbLink, err := gorm.Open(mysql.New(mysql.Config{
24         SkipInitializeWithVersion: true,
25         Conn: db,
26     }), &gorm.Config{})
27     if nil != err {
28         log.Fatalf("Init DB with sqlmock failed, err %v", err)
29     }
30 
31     repo := Repository{
32         db: dbLink,
33     }
34 
35     // create mock record
36     const sqlSelectAll = `SELECT * FROM "blogs"`
37     mock.ExpectQuery(sqlSelectAll).WillReturnRows(sqlmock.NewRows(nil))
38 
39     goods, err := repo.ListAll()
40     assert.Nil(t, err)
41     assert.Nil(t, goods)
42 }
View Code

 

 

  所有UT代碼地址:this


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM