Go基础学习11-测试工具gomock和monkey的使用

文章目录

  • 基础回顾
  • Mock
    • Mock是什么
    • 安装gomock
    • Mock使用
      • 1. 创建user.go源文件
      • 2. 使用mockgen生成对应的Mock文件
      • 3. 使用mockgen命令生成后在对应包mock下可以查看生成的mock文件
      • 4. 编写测试代码
      • 5. 运行代码并查看输出
  • Gomonkey
    • Gomonkey优势
    • 安装
    • 使用
      • 对函数进行monkey
      • 对结构体中方法进行monkey
      • 对全局变量进行monkey

基础回顾

测试:
在这里插入图片描述在这里插入图片描述
● 建立完善的回归测试,如果采用微服务,可以限制测试范围,只需要保证独立的微服务内部功能正常,因为微服务对外暴露的是接口。
● 为什么要将测试文件和源文件写到一个包中:便于测试包级别的示例,方法。

  • go test 命令是一个按照一定约定和组织的测试代码驱动测试,在包目录下 所有以_test.go结尾的文件都会被视为测试文件。并且 _test.go结尾的文件不会被go build 编译到可执行文件中。

Mock

Mock是什么

Mock是单元测试中常见的一种技术,就是在测试过程中,对于一些不容易构造或者获取的对象,创建一个Mock对象来模拟对象的行为,从而把测试与测试边界以外的对象隔离开。
优点:
团队并行工作
测试驱动开发 TDD (Test-Driven Development)
测试覆盖率
隔离系统
缺点

  • Mock不是万能的,使用Mock也存在着风险,需要根据项目实际情况和具体需要来确定是否选用Mock。
  • 测试过程中如果大量使用Mock,mock测试的场景失去了真实性,可能会导致在后续的系统性测试时才发现bug,使得缺陷发现的较晚,可能会造成后续修复成本更大

Mock广泛应用于接口类的方法的生成。 针对接口可以使用mock,针对不是接口的函数或者变量使用下面的monkey。

安装gomock

go get github.com/golang/mock/gomock
go get github.com/golang/mock/mockgen
go install github.com/golang/mock/mockgen@v1.6.0

安装完成后执行mockgen命令查看是否生效

Mock使用

1. 创建user.go源文件

// Package mock  ------------------------------------------------------------
// @file      : user.go
// @author    : WeiTao
// @contact   : 15537588047@163.com
// @time      : 2024/10/4 13:16
// ------------------------------------------------------------
package mockimport "context"type User struct {Mobile   stringPassword stringNickName string
}type UserServer struct {Db UserData
}func (us *UserServer) GetUserByMobile(ctx context.Context, mobile string) (User, error) {user, err := us.Db.GetUserByMobile(ctx, mobile)if err != nil {return User{}, err}if user.NickName == "TestUser" {user.NickName = "TestUserModified"}return user, nil
}type UserData interface {GetUserByMobile(ctx context.Context, mobile string) (User, error)
}

上述代码中的UserData是一个Interface{}类型,后面使用Mock生成的对象就是此接口对象,生成后可以将UserData接口中的方法GetUserByMoblie方法实现黑盒,从而无需关注具体实现细节,直接可以设置此函数对应的返回值。

2. 使用mockgen生成对应的Mock文件

mockgen使用:

// 源码模式mockgen -source 需要mock的文件名 -destination 生成的mock文件名 -package 生成mock文件的包名
// 参考示例:
mockgen -source user.go -destination=./mock/user_mock.go -package=mock

3. 使用mockgen命令生成后在对应包mock下可以查看生成的mock文件

// Code generated by MockGen. DO NOT EDIT.
// Source: user.go// Package mock is a generated GoMock package.
package mockimport (context "context"reflect "reflect"gomock "github.com/golang/mock/gomock"
)// MockUserData is a mock of UserData interface.
type MockUserData struct {ctrl     *gomock.Controllerrecorder *MockUserDataMockRecorder
}// MockUserDataMockRecorder is the mock recorder for MockUserData.
type MockUserDataMockRecorder struct {mock *MockUserData
}// NewMockUserData creates a new mock instance.
func NewMockUserData(ctrl *gomock.Controller) *MockUserData {mock := &MockUserData{ctrl: ctrl}mock.recorder = &MockUserDataMockRecorder{mock}return mock
}// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockUserData) EXPECT() *MockUserDataMockRecorder {return m.recorder
}// GetUserByMobile mocks base method.
func (m *MockUserData) GetUserByMobile(ctx context.Context, mobile string) (User, error) {m.ctrl.T.Helper()ret := m.ctrl.Call(m, "GetUserByMobile", ctx, mobile)ret0, _ := ret[0].(User)ret1, _ := ret[1].(error)return ret0, ret1
}// GetUserByMobile indicates an expected call of GetUserByMobile.
func (mr *MockUserDataMockRecorder) GetUserByMobile(ctx, mobile interface{}) *gomock.Call {mr.mock.ctrl.T.Helper()return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByMobile", reflect.TypeOf((*MockUserData)(nil).GetUserByMobile), ctx, mobile)
}

观察上述Mock文件可以看到实现了GetUserByMobile方法等。

4. 编写测试代码

// ------------------------------------------------------------
// package test
// @file      : user_test.go
// @author    : WeiTao
// @contact   : 15537588047@163.com
// @time      : 2024/10/4 13:23
// ------------------------------------------------------------
package mockimport ("context""github.com/golang/mock/gomock""testing"
)func TestGetUserByMobile(t *testing.T) {// mock准备工作ctl := gomock.NewController(t)defer ctl.Finish()userData := NewMockUserData(ctl)userData.EXPECT().GetUserByMobile(gomock.Any(), "15023076751").Return(User{Mobile:   "15023076751",Password: "123456",NickName: "TestUser",},nil,)// 实际调用过程userServer := UserServer{Db: userData,}user, err := userServer.GetUserByMobile(context.Background(), "15023076751")// 判断过程if err != nil {t.Error("GetUserByMobile error:", err)}if user.Mobile != "15023076751" || user.Password != "123456" || user.NickName != "TestUserModified" {t.Error("GetUserByMobile result is not expected.")}
}

5. 运行代码并查看输出

GOROOT=/usr/local/go #gosetup
GOPATH=/home/wt/Backend/go/goprojects #gosetup
/usr/local/go/bin/go test -c -o /home/wt/.cache/JetBrains/GoLand2024.2/tmp/GoLand/___TestGetUserByMobile_in_golearndetail_test_mock.test golearndetail/test/mock #gosetup
/usr/local/go/bin/go tool test2json -t /home/wt/.cache/JetBrains/GoLand2024.2/tmp/GoLand/___TestGetUserByMobile_in_golearndetail_test_mock.test -test.v=test2json -test.paniconexit0 -test.run ^\QTestGetUserByMobile\E$
=== RUN   TestGetUserByMobile
--- PASS: TestGetUserByMobile (0.00s)
PASSProcess finished with the exit code 0

Gomonkey

Gomonkey优势

上面使用mockgen生成对应的mock文件缺点非常明显,只能对于接口类的函数进行mock,然而实际项目并非所有函数都是接口类函数,大部分是内部使用的临时函数以及变量等,此时对这些函数以及变量无法使用mockgen生成对应的mock文件,此时可以使用另一个工具gomonkey
链接:https://github.com/agiledragon/gomonkey

安装

参考官网安装说明:

$ go get github.com/agiledragon/gomonkey/v2@v2.11.0

使用

对函数进行monkey

  1. 编写函数
package monkeyfunc networkCompute(a, b int) (int, error) {c := a + breturn c, nil
}func compute(a, b int) (int, error) {c, err := networkCompute(a, b)return c, err
}
  1. 编写测试用例
// 对函数进行mock
func Test_compute(t *testing.T) {patches := gomonkey.ApplyFunc(networkCompute, func(a, b int) (int, error) {return 8, nil})defer patches.Reset()sum, err := compute(1, 2)if err != nil {t.Error("Error occurred:", err)}fmt.Printf("sum : %d\n", sum)if sum != 8 {t.Error("Error occurred in sum:", err)}
}

在使用gomonkey运行测试用例的时候,直接run会报内联错误,解决方法有两个:

  • 在终端执行命令go test时加上参数:go test -gcflags=all=-l
  • 在Goland编辑器添加对应go运行变量:-gcflags=all=-l
  • 在这里插入图片描述
    加上 "all=-N -l"和”=all=-l"效果相同。
  1. 运行结果
/usr/local/go/bin/go tool test2json -t /home/wt/.cache/JetBrains/GoLand2024.2/tmp/GoLand/___7Test_compute_in_golearndetail_test_monkey.test -test.v=test2json -test.paniconexit0 -test.run ^\QTest_compute\E$
=== RUN   Test_compute
sum : 8
--- PASS: Test_compute (0.00s)
PASSProcess finished with the exit code 0

对结构体中方法进行monkey

  1. 编写代码
type Compute struct{}func (c *Compute) NetworkCompute(a, b int) (int, error) {sum := a + breturn sum, nil
}func (c *Compute) Compute(a, b int) (int, error) {sum, err := c.NetworkCompute(a, b)return sum, err
}
  1. 编写测试用例
// 对结构体中的方法进行mock
func Test_Compute_NetworkCompute(t *testing.T) {var compute *Computepatches := gomonkey.ApplyMethod(reflect.TypeOf(compute), "NetworkCompute", func(_ *Compute, a, b int) (int, error) {return 10, nil})defer patches.Reset()compute = &Compute{}sum, err := compute.Compute(3, 2)if err != nil {t.Error("Error occurred:", err)}fmt.Printf("sum : %d\n", sum)if sum != 10 {t.Error("Error occurred in sum:", err)}
}
  1. 运行结果
/usr/local/go/bin/go tool test2json -t /home/wt/.cache/JetBrains/GoLand2024.2/tmp/GoLand/___2Test_Compute_NetworkCompute_in_golearndetail_test_monkey.test -test.v=test2json -test.paniconexit0 -test.run ^\QTest_Compute_NetworkCompute\E$
=== RUN   Test_Compute_NetworkCompute
sum : 10
--- PASS: Test_Compute_NetworkCompute (0.00s)
PASSProcess finished with the exit code 0

对全局变量进行monkey

代码展示:

var num = 5// 对变量进行mock
func Test_GlobalVal(t *testing.T) {patches := gomonkey.ApplyGlobalVar(&num, 10)defer patches.Reset()if num != 10 {t.Error("Error occurred in num:mock failure", num)}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/438909.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Chat登录时出现SSO信息出错的解决方法

目录 1. 问题所示2. 问题所示3. 解决方法 1. 问题所示 此贴主要是总结回顾,对此放置在运维专栏 出现如下问题,很懵,以为是节点挂了还是网址蹦了 一直刷新,登录之后就出现这个问题 2. 问题所示 对于SSO,也就是单点登…

深度学习项目----用LSTM模型预测股价(包含LSTM网络简介,代码数据均可下载)

前言 前几天在看论文,打算复现,论文用到了LSTM,故这一篇文章是小编学LSTM模型的学习笔记;LSTM感觉很复杂,但是结合代码构建神经网络,又感觉还行;本次学习的案例数据来源于GitHub,在…

4.4章节python中循环结构得互相嵌套:常用于属于图形(长方形、三角形、菱形)

一、定义和注意事项 在Python中,循环结构(如for循环和while循环)可以互相嵌套。嵌套循环意味着一个循环内部包含另一个循环。这在处理多维数据或需要执行多次迭代的任务时非常有用。 注意: 1.缩进:在Python中&…

实施威胁暴露管理、降低网络风险暴露的最佳实践

随着传统漏洞管理的发展,TEM 解决了因攻击面扩大和安全工具分散而产生的巨大风险。 主动式 TEM 方法优先考虑风险并与现有安全工具无缝集成,使组织能够在威胁被有效利用之前缓解威胁。 为什么威胁暴露管理 (TEM) 在现代网络安全策略中变得至关重要&…

商家营销工具架构升级总结

今年以来,商家营销工具业务需求井喷,需求数量多且耗时都比较长,技术侧面临很大的压力。因此这篇文章主要讨论营销工具前端要如何应对这样大规模的业务需求。 问题拆解 我们核心面对的问题主要如下: 1. 人力有限 我们除了要支撑存量…

C语言 | Leetcode C语言题解之题451题根据字符出现频率排序

题目: 题解: #define HASH_FIND_CHAR(head, findint, out) HASH_FIND(hh, head, findint, sizeof(char), out) #define HASH_ADD_CHAR(head, intfield, add) HASH_ADD(hh, head, intfield, sizeof(char), add)struct HashTable {char key;int val;UT_ha…

《数据密集型应用系统设计》笔记——第二部分 分布式数据系统(ch5-9)

第5章 数据复制 目的: 地理位置更近,降低延迟故障冗余提高读吞吐量 主节点与从节点(主从复制) 主从复制: 写请求发送给主节点,主节点将新数据写入本地存储;主节点将数据更改作为复制的日志发送…

SAP学习笔记 - Basis01 - 创建Client ,拷贝Client

最近工作当中用到了Client间数据移送的内容,想把自己的虚机给弄两个Client。 最后也没完全弄成,先把过程整理一下,以后有空接着弄。 目录 1,SALE - 新建逻辑系统 2,SCC4 - 分配Client到集团 3,RZ10 - 取…

python-FILIP/字符串p形编码/数字三角形

一:FILIP 题目描述 给你两个十进制正整数 a,b​,输出将这两个数翻转后的较大数。 「翻转」在本题中的定义详见「说明 / 提示」部分。输入 第一行,两个十进制正整数 a,b。输出 第一行,a 和 b 翻转后的较大数。样例输入1 734 893 样…

Microsoft Edge 五个好用的插件

🐣个人主页 可惜已不在 🐤这篇在这个专栏 插件_可惜已不在的博客-CSDN博客 🐥有用的话就留下一个三连吧😼 目录 Microsoft Edge 一.安装游览器 ​编辑 二.找到插件商店 1.打开游览器后,点击右上角的设置&#…

【深度学习基础模型】深度残差网络(Deep Residual Networks, DRN)详细理解并附实现代码。

【深度学习基础模型】深度残差网络(Deep Residual Networks, DRN)详细理解并附实现代码。 【深度学习基础模型】深度残差网络(Deep Residual Networks, DRN)详细理解并附实现代码。 文章目录 【深度学习基础模型】深度残差网络&a…

Python小示例——质地不均匀的硬币概率统计

在概率论和统计学中,随机事件的行为可以通过大量实验来研究。在日常生活中,我们经常用硬币进行抽样,比如抛硬币来决定某个结果。然而,当我们处理的是“质地不均匀”的硬币时,事情就变得复杂了。质地不均匀的硬币意味着…

Spring Boot中线程池使用

说明:在一些场景,如导入数据,批量插入数据库,使用常规方法,需要等待较长时间,而使用线程池可以提高效率。本文介绍如何在Spring Boot中使用线程池来批量插入数据。 搭建环境 首先,创建一个Spr…

每日学习一个数据结构-树

文章目录 树的相关概念一、树的定义二、树的基本术语三、树的分类四、特殊类型的树五、树的遍历六、树的应用场景 树的遍历一、前序遍历二、中序遍历三、后序遍历使用java代码实现遍历总结 树的相关概念 树是一种重要的非线性数据结构,在计算机科学中有着广泛的应用…

24-10-4-读书笔记(二十四)-《一个孤独漫步者的遐想》下([法] 让·雅克·卢梭 [译]陈阳)

文章目录 《一个孤独漫步者的遐想》下([法] 让雅克卢梭 [译]陈阳)目录阅读笔记记录总结 《一个孤独漫步者的遐想》下([法] 让雅克卢梭 [译]陈阳) 十月第四篇,这次应该能拿到流量券吧!《一个孤独漫步者的遐想…

A Learning-Based Approach to Static Program Slicing —— 论文笔记

A Learning-Based Approach to Static Program Slicing OOPLSA’2024 文章目录 A Learning-Based Approach to Static Program Slicing1. Abstract2. Motivation(1) 为什么需要能处理不完整代码(2) 现有方法局限性(3) 验证局限性: 初步实验研究实验设计何为不完整代码实验结果…

C#串口温度读取

背景:每天学点,坚持 要安装好虚拟串口和modbus poll,方便调试(相关资源在文末,也可以私信找我要) 传感器部分使用的是达林科技的DL11B-MC-D1,当时42软妹币买的(官网上面有这个传感…

网络编程(12)——完善粘包处理操作(id字段)

十二、day12 之前的粘包处理是基于消息头包含的消息体长度进行对应的切包操作,但并不完整。一般来说,消息头仅包含数据域的长度,但是如果要进行逻辑处理,就需要传递一个id字段表示要处理的消息id,当然可以不在包头传i…

Linux网络编程

文章目录 参考资料在前1. 前置知识2. 进程概述2.1 fork()函数2.2 守护进程 3. 浅谈printf()函数与write()函数3.1 printf()函数缓存问题3.2 write()函数思考 4. 网络编程剖析4.1 listen()监听套接字4.2 阻塞/非阻塞IO4.3 同步/异步IO4.4 TCP/IP设计4.4.1 三次握手4.4.2 四次挥手…

机器人的性能指标

1. 负荷能力 负荷能力负荷能力是指机器人在满足其他性能要求的情况下,能够承载的负荷重量。例如,一台机器人的最大负荷能力可能远大于它的额定负荷能力,但是达到最大负荷时,机器人的工作精度可能会降低,可能无法准确地沿着预定的轨迹运动,或者产生额外的偏差。机器人的负荷量与…