目录

使用gomock包

1. 安装mockgen

2. 定义接口

3. 生成mock文件

4. 在单测中使用mock的函数

5. gomock 包的使用问题

使用gomonkey包

1. mock 一个包函数

2. mock 一个公有成员函数

3. mock 一个私有成员函数


使用gomock包

1. 安装mockgen

go get -u github.com/golang/mock/gomock
go get -u github.com/golang/mock/mockgen

2. 定义接口

比如我们想要 mock 一个 infra 包中的名为 GetFromRedis 的函数:

// pkg/infra/infra.go 
package infrafunc GetFromRedis(cli *redis.Client, key string) (string, error) {// 为什么这里要打印一下呢,因为一行会触发内联优化,影响单测结果...fmt.Println("cannot reach here if is ut")return cli.Get(context.Background(), key).Result()
}

那需要定义一个 interface 将此GetFromRedis 的函数封装起来:

type Redis interface {GetFromRedis(cli *redis.Client, key string) (string, error)
}

这个 interface 必须定义,否则 gomock 无法生成 mock 方法。

3. 生成mock文件

在命令行执行:

mockgen -source=pkg/infra/infra.go -destination=pkg/infra/mock/mock_infra.go -package=mock

其中 source 为待 mock 的文件,destination 为 mock 文件生成的位置。

mockgen 工具将为你生成 pkg/infra/mock/mock_infra.go 文件。

4. 在单测中使用mock的函数

mock 就可以这样写:

// pkg/infra/infra_test.gofunc Test_gomock(t *testing.T) {// 创建gomock控制器ctrl := gomock.NewController(t)defer ctrl.Finish()var redisCli *redis.Client// 创建模拟对象m := mock.NewMockRedis(ctrl)// 定义预期行为m.EXPECT().GetFromRedis(redisCli, "test_key"). // 模拟入参Return("test value", nil)           // 模拟返回值// 调用被测方法,获取实际结果resVal, err := m.GetFromRedis(redisCli, "test_key")assert.NoError(t, err)assert.Equal(t, "test value", resVal)
}

5. gomock 包的使用问题

  • 用起来很麻烦,要定义 interface,还要执行 mockgen 命令
  • 无法 mock 私有成员的函数

使用gomonkey包

当你用了 gomonkey 之后!就再也不会想用 gomock 了!(gomonkey打钱)

1. mock 一个包函数

还是上述例子,比如我们想要 mock 一个 infra 包中的名为 GetFromRedis 的函数:

// pkg/infra/infra.go 
package infrafunc GetFromRedis(cli *redis.Client, key string) (string, error) {fmt.Println("cannot reach here if is ut")return cli.Get(context.Background(), key).Result()
}

无需前置操作,直接在单测中写 mock 代码:

// pkg/infra/infra_test.gofunc Test_gomonkey_function(t *testing.T) {// 创建gomonkey对象, 模拟GetFromRedis预期行为patch := gomonkey.ApplyFunc(GetFromRedis, func(_ *redis.Client, _ string) (string, error) {return "test value", nil})defer patch.Reset()// 调用被测方法,获取实际结果resVal, err := GetFromRedis(&redis.Client{}, "test_key")assert.NoError(t, err)assert.Equal(t, "test value", resVal)
}

ApplyFunc 的第一个参数是被测函数,第二个参数是预期的行为函数(注意入参出参定义要与被测函数完全一致)

当然你还可以用 patch 去 mock 更多函数:

patch.ApplyFunc(...)

2. mock 一个公有成员函数

比如我们有如下公有成员函数 PublicRedisHandler.GetFromRedis:

// pkg/infra/infra.go 
package infratype PublicRedisHandler struct {cli *redis.Client
}func NewPublicRedisHandler(cli *redis.Client) *PublicRedisHandler {return &PublicRedisHandler{cli: cli,}
}func (r *PublicRedisHandler) GetFromRedis(key string) (string, error) {fmt.Println("cannot reach here if is ut")return r.cli.Get(context.Background(), key).Result()
}

在单测中写法为:

// pkg/infra/infra_test.gofunc Test_gomonkey_public_member(t *testing.T) {// mock 公共成员方法patch := gomonkey.ApplyMethod(reflect.TypeOf(&PublicRedisHandler{}), "GetFromRedis", func(_ *PublicRedisHandler, key string) (string, error) {return "test value", nil})defer patch.Reset()// 调用被测方法,获取实际结果r := NewPublicRedisHandler(&redis.Client{})resVal, err := r.GetFromRedis("test_key")assert.NoError(t, err)assert.Equal(t, "test value", resVal)
}

其中 ApplyMethod 的第一个参数是成员的类型,第二个参数是函数名称,第三个参数是预期的行为函数(注意入参的第一个参数必须是成员对象)

3. mock 一个私有成员函数

那如果我们想要 mock 的函数是一个私有成员下的函数呢:

// pkg/infra/infra.go 
package infratype privateRedisHandler struct {cli *redis.Client
}func newewPrivateRedisHandler(cli *redis.Client) *privateRedisHandler {return &privateRedisHandler{cli: cli,}
}func (r *privateRedisHandler) GetFromRedis(key string) (string, error) {// 为什么这里要打印一下呢,因为一行会触发内联优化,影响单测结果...fmt.Println("cannot reach here if is ut")return r.cli.Get(context.Background(), key).Result()
}

那么写法为:

// pkg/infra/infra_test.gofunc Test_gomonkey_private_member(t *testing.T) {// mock 私有成员方法patch := gomonkey.ApplyPrivateMethod(reflect.TypeOf(&privateRedisHandler{}), "GetFromRedis", func(_ *privateRedisHandler, key string) (string, error) {return "test value", nil})defer patch.Reset()// 调用被测方法,获取实际结果r := newPrivateRedisHandler(&redis.Client{})resVal, err := r.GetFromRedis("test_key")assert.NoError(t, err)assert.Equal(t, "test value", resVal)
}

其中 ApplyPrivateMethod 的第一个参数是成员的类型,第二个参数是函数名称,第三个参数是预期的行为函数(注意入参的第一个参数必须是成员对象)

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

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

相关文章

html实现登录与注册功能案例(不写死且只使用js)

目录 案例需求 实现思路 代码参考 login.html register.html 运行效果 升级思路 案例需求 需要一个登录界面和注册页面实现一个较为完整的登录注册功能 1.登录界面没有登录限制需求(降低难度),实现基本的登录判断需求,弹窗…

PHP is the best language.

PHP很好写。 众所周知Python很好写,Python 也能开发 Web 应用,但和 PHP 相比,在“直接处理网页”这件事上,PHP 更加贴近底层和原生。 想快速搭建原型或者 B 端后台工具,不妨用 PHP Laravel 来搞,真的很香…

Mybatis-Plus 在 getOne() 的时候要加上 .last(“limit 1“)

1.先写结论: 1.为了确保 SQL 查询只返回一条记录(当查询返回多条时会报错->多为代码本身问题)。 2.防止数据库执行全表扫描 3.参考网址:问题记录:MyBatis-Plus 中 ServiceImpl 类的 getOne_mybatis_无他&唯手熟尔-2048…

C语言:二分搜索函数

一、二分搜索基本概念 二分搜索(Binary Search)是一种在有序数组中查找特定元素的高效算法,时间复杂度为O(log n)。 基本特点: 仅适用于有序数组(升序或降序) 每次比较将搜索范围减半 比线性搜索(O(n))…

[前端AI]LangChain.js 和 Next.js LLM构建——协助博客撰写和总结助手

LangChain.js 和 Next.js LLM 后端应用于协助博客撰写和总结领域是一个非常实用的方向!这涉及到理解和处理文本内容,并生成新的、有结构的信息。 根据您之前提供的代码和需求,我们可以在此基础上进行更具针对性的功能规划和技术实现。 博客…

用 GitHub Issues 做任务管理和任务 List,简单好用!

说实话,我平时也是一个人写代码,每次开完会整理任务最麻烦: 一堆事项堆在聊天里、文档里,或者散落在邮件里…… 为了理清这些,我通常会做一份 List,标好优先级,再安排到每日的工作里 虽然这个…

每日算法刷题Day35 6.22:leetcode枚举技巧枚举中间2道题,用时1h

枚举中间 对于三个或者四个变量的问题&#xff0c;枚举中间的变量往往更好算。 为什么&#xff1f;比如问题有三个下标&#xff0c;需要满足 0≤i<j<k<n&#xff0c;对比一下&#xff1a; 枚举 i&#xff0c;后续计算中还需保证 j<k。 枚举 j&#xff0c;那么 i 和…

【教学类-18-06】20250623蒙德里安黑白七款合并WORD(500张、无学号)

背景需要 客户买了蒙德里安黑白格子7种尺寸,但是不需要学号方块,并指定要WORD 设计思路 【教学类-18-05】20241118正方形手工纸(蒙德里安-风格派-红黄蓝黑白)-CSDN博客文章浏览阅读1.3k次,点赞29次,收藏18次。【教学类-18-05】20241118正方形手工纸(蒙德里安-风格派-红…

langchain--(4)

7 Embedding文本向量化 Embedding文本向量化是一种将非结构化文本转化为低维、连续数值向量的技术,旨在通过数学方式捕捉文本的语义、语法或特征信息,从而让机器更高效地处理语言任务。其核心思想源于流形假设(Manifold Hypothesis),即认为高维原始数据(如文本)实际隐含…

DMDRS部署实施手册(ORACLE=》DM)

DMDRS部署实施手册&#xff08;ORACLE》DM&#xff09; 1 同步说明2 DMDRS安装3 数据库准备3.1 源端准备3.1.1 开启归档日志和附加日志3.1.2 关闭回收站3.1.3 创建同步用户 3.2 目标准备3.2.1 创建同步用户 4 DMDRS配置4.1 源端配置4.2 目标配置 5 DMDRS启动5.1 启动源端服务5.…

十(1)作业:sqli-labs重点关卡

参考文章&#xff1a;详细sqli-labs&#xff08;1-65&#xff09;通关讲解-CSDN博客 第1关&#xff1a; 输入 &#xff1a; ?id3 输入 &#xff1a; ?id2 当输入的数字不同&#xff0c;页面的响应也不同&#xff0c;说明&#xff0c;输入的内容被带入到数据库里查询了 输…

Python 爬虫入门 Day 7 - 复盘 + 实战挑战日

Python 第二阶段 - 爬虫入门 &#x1f3af; 本周知识回顾 网络请求与网页结构基础 HTML解析入门&#xff08;使用 BeautifulSoup&#xff09; 实现爬虫多页抓取与翻页逻辑 模拟登录爬虫与 Session 维持 使用 XPath 进行网页解析&#xff08;lxml XPath&#xff09; 反爬虫应对…

WebRTC(七):媒体能力协商

目的 在 WebRTC 中&#xff0c;每个浏览器或终端支持的音视频编解码器、分辨率、码率、帧率等可能不同。媒体能力协商的目的就是&#xff1a; 确保双方能“听得懂”对方发的媒体流&#xff1b;明确谁发送、谁接收、怎么发送&#xff1b;保障连接的互操作性和兼容性。 P2P的基…

可信启动方案设计

安全之安全(security)博客目录导读 目录 一、引言 二、关键数据(Critical Data) 三、度量槽(Measurement Slot) 四、可信启动后端 1、事件日志(Event Log) 2、离散型 TPM(Discrete TPM) 3、RSE(运行时安全引擎) 五、平台接口 平台接口的职责: 1、函数:b…

✨通义万相2.1深度解析:AI视频生成引擎FLF2V-14B全流程指南(命令行参数+模型架构+数据流)

&#x1f31f; 从零详解&#xff1a;如何用AI模型生成视频&#xff1f;命令行、模型结构、数据流全解析&#xff01; 本文通过一个实际案例&#xff0c;详细解析使用AI模型生成视频的整个流程。从命令行参数解读到模型结构&#xff0c;再到数据在模型间的流动&#xff0c;一步步…

在 TypeScript 前端中使用 Umi-Request 调用 Java 接口的完整指南

下面我将详细介绍如何在基于 TypeScript 的前端项目中使用 umi-request 调用 IntelliJ IDEA 中开发的 Java 接口&#xff0c;包括完整的实现方案和代码示例。 整体方案设计 一、Java 后端接口准备 1. 创建 Spring Boot 控制器 // src/main/java/com/example/demo/controller…

GO Gin Web框架面试题及参考答案

目录 Gin 与 net/http 有哪些主要区别?为什么选择 Gin? 如何使用 Gin 启动一个 HTTP 服务并设置默认路由? Gin 的默认路由和自定义路由器组是如何工作的? 如何在 Gin 中绑定请求参数(Query、Form、JSON、XML)? 如何在 Gin 中使用中间件?中间件执行顺序是怎样的? …

asp.net core Razor动态语言编程代替asp.net .aspx更高级吗?

For Each item In products<tr><td>item.Id</td><td>item.Name</td><td>item.Price.ToString("C")</td></tr>Next为什么要用<tr> ? 在Blazor的Razor语法中&#xff0c;使用<tr>是为了在VB.NET代码块中…

css语法中的选择器与属性详解:嵌套声明、集体声明、全局声明、混合选择器

嵌套声明 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>嵌套声明</title> <!-- 这里p span 的含义是p标签下面的span标签 所以有嵌套关系--><style>p span {font-weight:…

Linux 系统中,/usr/bin/ 和/bin/的区别?

在 Linux 系统中&#xff0c;/bin/ 和 /usr/bin/ 都是存放可执行程序&#xff08;命令&#xff09;的目录&#xff0c;但它们在历史定位、用途、挂载策略和系统设计上有一定区别。 ✅ 快速对比总结 项目/bin//usr/bin/全称含义binary&#xff08;核心二进制&#xff09;user b…