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