最近帮忙给一个项目补充单元测试,有一些单测比较不好写, 到网上查了一下,发现有很多有意思的写法,特此总结一下

net.dial 方法的单测

如果我们代码里面使用了net.Dial()去访问外部的tcp or udp 端口,然后使用返回的Conn对象去处理里面的数据,我们该如何对这个Conn对象进行mock呢?

  1. 最好想的办法是自己实现一个server端,使用net.Listen()本地的一个端口,并且撰写自己的handler方法,在handler方法里面返回想要的值。最后在单测里面直接调用这个端口就可以在返回的conn对象中获取到mock的值了。
    下面写一个简单的例子
go func() {conn, err := net.Dial("tcp", 3000)if err != nil {xxx}defer conn.close()xxx
}
l, err := net.Listen("tcp", 3000)
defer l.Close()
for {conn, err := l.Accept()defer conn.Close()handle(conn)
}
  1. 其实最简单的方法是这种
server, client := net.Pipe()
go func(){server.Close()
}()client.Close()

mock 本地service 和 第三方 package

我们直接用代码说话吧

  1. 我们使用如下的service来当做db(第三方服务)
package userdb// db act as a dummy package level database.
var db map[string]bool// init initialize a dummy db with some data
func init() {db = make(map[string]bool)db["test@dummy.org"] = truedb["test1@example.com"] = true
}// UserExists check if the User is registered with the provided email.
func UserExists(email string) bool {if _, ok := db[email]; !ok {return false}return true
}
  1. 我们写个方法调用上面的服务

package simpleserviceimport ("fmt""log""github.com/ankur-anand/mocking-demo/userdb"
)// User encapsulate a user in the system.
type User struct {Name     string `json:"name"`Email    string `json:"email"`UserName string `json:"user_name"`
}// RegisterUser will register a User if only User has not been previously
// registered.
func RegisterUser(user User) error {// check if user is already registeredfound := userdb.UserExists(user.Email)if found {return fmt.Errorf("email '%s' already registered", user.Email)}// carry business logic and Register the user in the systemlog.Println(user)return nil
}
  1. 然后再为上面的RegisterUser写一个单元测试
package simpleserviceimport "testing"func TestCheckUserExist(t *testing.T){user := User{Name: "Ankur Anand"Email: "anand@example.com"UserName: "anand"}err := RegisterUser(user)if err == nil {t.Error("Expect Register User to throw and error got nil")}
}

我们可以看到上面 2 中的 RegisterUser方法内部使用了一个 userdb.UserExist访问数据库的方法, 按理说
我们无法调用RegisterUser进行单元测试,除非可以调用这个数据库方法。

下面我们就来解决这个问题
我们需要重构我们的代码。

  1. 我们定义一个interface
type registrationPreChecker interface {userExists(string) bool
}
  1. 然后我们定义一个struct实现这个接口
type regPreCheck struct {}
func (r regPreCheck) userExist(email string) bool {return userdb.UserExist(email)
}
  1. 我们定义一个package-level 的registrationPreCheckerinterface 类型的变量, 并在init方法里面调用它
var regPreCond registrationPreCheckerfunc init() {regPreCond = regPreCheck{}
}
  1. 最后我们重写一下RegisterUser代码
func RegisterUser(user User) error {found := regPreCond.userExist(user.Email)if found {return fmt.Errorf("email '%s' already registered", user.Email)}// carry business logic and Register the user in the systemlog.Println(user)return nil
}

事前准备做完了,我们来写下mock脚本

var userExistsMock func(email string) bool
type preCheckMock struct{}
func (u preCheckMock) userExists(email string) bool {return userExistsMock(email)
}func TestRegisterUser(t *testing.T) {user := User {Name:     "Ankur Anand"Email:    "anand@example.com"UserName: "anand"}regPreCond = preCheckMock{}userExistsMock = func(email string) bool {return false}err := RegisterUser(user)if err != nil {t.Fatal(err)}userExistsMock = func(email string) bool {return true}err = RegisterUser(user)if err == nil {t.Error("Expected Register User to throw and error got nil")}
}

Mock 实现返回了 userExistsMock方法,而不是简单的trueorfalse这个是为了要在运行期分配mock, 而不是在编译器分配
你可以在 Test 方法里面找到具体的mock function

但是我们还没结束,上面的方式还是有问题的。

我们在上面使用了全局变量, 虽然我们没有在实际过程中更新 这个全局变量, 但是这会破坏并发测试

想要避免上述问题其实也很简单, 直接把registrationPreChecker的实例通过参数传入RegisterUser()方法里面就可以了

gomock package

其实gomock package 也是以自定义interface为入口,将自定义返回数据注入到mockobject 里面去。实现原理基本与上面一样, 使用gomock当然会简单一些,不过个人还是偏向于上面那种纯手动的。。

如何用golang编写单元测试用例相关推荐

  1. 2、编写单元测试用例,对用户注册功能的DAO层进行测试。(注意:测试用例应考虑成功和失败的情况)...

    我先对我做的测试进行说明: 对用户注册功能的DAO层进行测试,其实就是对UserDao中的saveUser(User user) 方法进行测试.我在我的测试方法中同时也用到了UserDao中的exit ...

  2. Java编程技巧之单元测试用例编写流程

    简介: 立足于"如何来编写单元测试用例",让大家"有章可循",快速编写出单元测试用例. 作者 | 常意 来源 | 阿里技术公众号 温馨提示:本文较长,同学们可收 ...

  3. 单元测试用例_前端单元测试实践

    一说到单元测试,可能对于业务一线同学来说,心理立马就会无形中有一种压迫感,心想 "业务都做不完了,写个球的单元测试,先保证功能完备,赶紧上线才是王道",这句话的核心是以业务为重,没 ...

  4. SAP UI5 应用开发教程之八十五 - 如何用 OPA5 编写测试用例来测试用户输入文本的功能试读版

    一套适合 SAP UI5 初学者循序渐进的学习教程 作者简介 Jerry Wang,2007 年从电子科技大学计算机专业硕士毕业后加入 SAP 成都研究院工作至今.Jerry 是 SAP 社区导师,S ...

  5. 85. 如何用 OPA5 编写测试用例来测试用户输入文本的功能

    SAP UI5 应用开发教程之八十五 - 如何用 OPA5 编写测试用例来测试用户输入文本的功能 本教程之前的系列文章,我们已经学习了如何在测试用例里,用代码的方式,来模拟用户点击 SAP UI5 表 ...

  6. 如何用xmind编写测试用例

    1.软件测试用例是什么呢? 测试用例就是为项目需求(即就是需求文档)而编写的一组测试输入,执行条件以及预期结果,来测试某个功能的程序是否满足需求文档所说明的,通常在编写测试用例的时候,需先要通过需求文 ...

  7. java 单元测试 异步_Java/Android编写异步的单元测试用例

    不写单元测试用例的程序员不是一个好CTO!!! 注:以下内容编码环境为AndroidStudio_2.4Preview6,测试框架 JUnit4.12 今天在研究 MVP_RxJava2_Retrof ...

  8. JUnit单元测试用例

    2019独角兽企业重金招聘Python工程师标准>>> 注意在编写测试用例的时候,要保持测试用例的独立性 一个原则,每条单元测试用例都必须独立运行,不能依靠其他测试用例,或者不能按照 ...

  9. ultraedit 运行的是试用模式_Wings面向企业级的单元测试用例自动编码引擎

    点击上方"蓝字"关注我们吧! 2020年7月30日,星云测试在TiD2020质量竞争力大会正式发布最新产品"Wings-企业级单元用例自动编码引擎".这是国际首 ...

最新文章

  1. 【Spark亚太研究院系列丛书】Spark实战高手之路-第一章 构建Spark集群(第二步)(4)...
  2. Leetcode 23 合并k个升序链表 (每日一题 20210722)
  3. torch 变量_python变量
  4. HTTP一个 TCP 连接可以发多少个 HTTP 请求等面试题
  5. Dominant Indices(CF 1009 F)
  6. Linux_基础_进程管理
  7. sysdig案例分析 - 哪些文件正在被进程访问
  8. nodejs 前端 返回数组给_互联网寒冬,一年经验字节跳动、虾皮、快手、拼多多前端面试总结...
  9. C# interview questions--- 国外大公司c#技术面试必看(总结贴一)
  10. linux 压缩工具
  11. 通讯录AddressBook
  12. Maxima 的基本微积分操作(链接)
  13. 2,词根 - 抓、拿
  14. Opencv中rect的功能应用
  15. 古老CPU启示录-意义重大的8008 芯片
  16. 华为服务器bios系统,华为服务器bios配置详解
  17. C++2022NOC之初赛数字规律
  18. sever and mirroring(服务器和镜像)是什么
  19. 【ArcGIS教程】(1)带有经纬度的EXCEL数据如何转换为shp矢量数据?
  20. python爬虫: 爬取拉勾网职位并分析

热门文章

  1. 三代测序序列比对利器-BLASR,更小更快更方便
  2. 微型计算机原理及其接口技术,微机原理及接口技术
  3. JSP+SQL饭店点菜与管理系统
  4. GBDT和Xgboost:原理、推导、比较
  5. 未能开启本地隧道服务器,IPV6隧道适配器始终未能打开,请大神帮忙看下
  6. 基于微信企业公司小程序设计与实现开题答辩PPT
  7. 信息设计软件+信息图表设计软件介绍
  8. threejs 草场足球运动视角(三)
  9. idea快速创建接口实现类快捷键
  10. Java计算圆的体积,面积