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:

Go单元测试实践Go单元测试实践
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:

Go单元测试实践Go单元测试实践
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:

Go单元测试实践Go单元测试实践
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:

Go单元测试实践Go单元测试实践
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

Original: https://www.cnblogs.com/nxnslc-blog/p/15236542.html
Author: 是凉城吖
Title: Go单元测试实践

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/516526/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 因势而变,因时而动,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang泛型(generic)的使用EP15

    事实上,泛型才是Go lang1.18最具特色的所在,但为什么我们一定要拖到后面才去探讨泛型?类比的话,我们可以想象一下给小学一年级的学生讲王勃的千古名篇《滕王阁序》,小学生有多大…

    Go语言 2023年5月25日
    030
  • go 自定义http.Client – 动态修改请求Body

    在对接Alexa Smart Home时,有的请求Payload中需要传入Access Token,但是这个Token是由OAuth2 Client管理的,封装Payload时并不…

    Go语言 2023年5月25日
    045
  • 对不起,我错了,这代码不好写

    hello,大家好呀,我是小楼。 前几天不是写了这篇文章《发现一个开源项目优化点,点进来就是你的了》嘛。 文章介绍了Sentinl的自适应缓存时间戳算法,从原理到实现都手把手解读了…

    Go语言 2023年5月25日
    057
  • gRPC,爆赞

    gRPC 这项技术真是太棒了,接口约束严格,性能还高,在 k8s 和很多微服务框架中都有应用。 作为一名程序员,学就对了。 之前用 Python 写过一些 gRPC 服务,现在准备…

    Go语言 2023年5月25日
    051
  • 服务注册与发现的原理和实现

    什么是服务注册发现? 对于搞微服务的同学来说,服务注册、服务发现的概念应该不会太陌生。 简单来说,当服务A需要依赖服务B时,我们就需要告诉服务A,哪里可以调用到服务B,这就是服务注…

    Go语言 2023年5月25日
    049
  • 浅谈MatrixOne如何用Go语言设计与实现高性能哈希表

    目录 MatrixOne数据库是什么? 哈希表数据结构基础 哈希表基本设计与对性能的影响 碰撞处理 链地址法 开放寻址法 Max load factor Growth factor…

    Go语言 2023年5月25日
    046
  • go泛型教程

    导读: 约束 使用方法 实现原理 跟其它语言的泛型进行对比 用例子学泛型 issues 泛型需满足 go1.18+ go使用interface作为约束,约束的意思是约束了这个泛型都…

    Go语言 2023年5月25日
    049
  • Excelize 2.5.0 正式发布,这些新增功能值得关注

    Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库,基于 ECMA-376,ISO/IEC 29500 国际标准。可以使用它来读取、写入由 Mic…

    Go语言 2023年5月25日
    055
  • go-zero单体服务使用泛型简化注册Handler路由

    一、Golang环境安装及配置Go Module https://go-zero.dev/cn/docs/prepare/golang-install mac OS安装Go 下载并…

    Go语言 2023年5月25日
    062
  • Go 语言快速开发入门

    需求 开发的步骤 linux下如何开发Go程序 MAC下如何开发Go程序 Golang执行流程分析 编译和运行说明 Go程序开发的注意事项 Go语言的转义字符(escapechar…

    Go语言 2023年5月25日
    072
  • 听说,99% 的 Go 程序员都被 defer 坑过

    先声明:我被坑过。 出问题就对了,这个小东西坏的很,一不留神就出错。 所以,面对这种情况,我们今天就不讲道理了。直接把我珍藏多年的代码一把梭,凭借多年踩坑经历和写 BUG 经验,我…

    Go语言 2023年5月25日
    065
  • Golang 源码解读 01、深入解析 strings.Builder、strings.Join

    本文从我的《The Go Programming Language》学习笔记中分离出,单独成一篇文章方便查阅参考。 strings.Builder 源码解析 存在意义 使用 (st…

    Go语言 2023年5月25日
    071
  • go语言调用everything的SDK接口

    介绍 官方SDK地址 本项目会将官方dll编译到可执行程序中,运行时无需考虑dll问题。 根据官方介绍,使用SDK前需要运行 everything程序。 执行 go build -…

    Go语言 2023年5月25日
    069
  • Go切片全解析

    目录结构:数组切片 底层结构 创建 普通声明 make方式 截取 边界问题 追加 拓展表达式 扩容机制 切片传递的坑 切片的拷贝 浅拷贝 深拷贝 var n [4]int fmt….

    Go语言 2023年5月25日
    067
  • Go语言基础之并发

    并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很重要的原因。 Go语言中的并发编程 并发与并行 并发:同一时间段内执行多个任务(你在用微…

    Go语言 2023年5月29日
    045
  • go-micro开发RPC服务的方法及其运行原理

    go-micro是一个知名的golang微服务框架,最新版本是v4,这篇文章将介绍go-micro v4开发RPC服务的方法及其运作原理。 基本概念 go-micro有几个重要的概…

    Go语言 2023年5月25日
    048
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球