当前位置: 首页 > news >正文

go语言中的GoMock

GoMock是一个Go框架。它与内置的测试包整合得很好,并在单元测试时提供了灵活性。正如我们所知,对具有外部资源(数据库、网络和文件)或依赖关系的代码进行单元测试总是很麻烦。

安装

为了使用GoMock,我们需要安装gomock包github.com/golang/mock/gomock和mockgen代码生成工具github.com/golang/mock/mockgen。使用这个命令行:

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

GoMock的使用遵循四个基本步骤:

  • 使用mockgen为你想模拟的接口生成一个模拟对象。
  • 在测试部分,创建一个gomock.Controller的实例,并把它传递给你的mock对象的构造函数以获得一个mock对象。
  • 在mock上调用EXPECT()来设置期望值和返回值。
  • 在模拟控制器上调用Finish()来断言模拟对象的期望。

开始

让我们创建一个这样的文件夹(本代码在 go1.16.15 版本下执行)

gomock_test
├── doer
│ └── doer.go
├── mocks
│ └── mock_doer.go
└── user├── user.go└── user_test.go

doer/doer.go

package doertype Doer interface {DoSomething(int, string) errorSaySomething(string) string
}

那么这里是我们在模拟Doer接口时要测试的代码。

user/user.go

package userimport "gomock_test/doer"const (filtered   = "OK"unfiltered = "spam"Nice       = "nice"Bad        = "bad"
)type User struct {// struct while mocking the doer interfaceDoer doer.Doer
}// method Use using it
func (u *User) Use() error {return u.Doer.DoSomething(123, "Hello GoMock")
}func (u *User) SaySomething(num int) string {if num == 3 {return u.Doer.SaySomething(unfiltered)}return u.Doer.SaySomething(filtered)
}type Student struct{}func (s *Student) DoSomething(_ int, _ string) error {panic("not implemented") // TODO: Implement
}func (s *Student) SaySomething(kata string) string {if kata == filtered {return Nice}return Bad
}

我们将把Doer的模拟放在一个包mocks中。我们首先创建一个包含我们的模拟实现的目录mocks,然后在doer包上运行mockgen:

mockgen -destination=../mocks/mock_doer.go -package=mocks gomock_test/doer Doer

NOTE: 在执行这步的时候,会报错:Failed to format generated source code: mocks/mock_doer.go:5:15: expected ‘;’, found '.’ 这个时候,我们只需要将打印出来的代码复制到我们对应的文件中即可。

当有大量的接口/包需要模拟时,为每个包和接口运行mockgen是一种乌托邦。为了缓解这个问题,可以将mockgen命令与go:generate放在一起。

go:generate mockgen -destination=../mocks/mock_doer.go -package=mocks gomock_test/doer Doer

我们必须自己创建目录模拟,因为GoMock不会为我们这样做,而是会以错误退出。

  • destination=…/mocks/mock_doer.go : 把生成的mocks放在这个路径下。
  • -package=mocks : 把生成的mocks放在这个包里
  • gomock_test/doer : 为这个包生成mocks。
  • Doer : 为这个接口生成mocks(必填),因为我们需要指定哪个接口来生成mocks。(如果需要的话,可以用逗号分隔的列表来指定多个接口。例如,Doer1, Doer2)

因为我们对mockgen的调用在我们的项目中放置了一个文件mocks/mock_doer.go。这就是这样一个生成的mock实现的样子:

// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/timliudream/go-test/gomock_test/doer (interfaces: Doer)// Package github.com/timliudream/go-test/gomock_test/mocks is a generated GoMock package.
package mocksimport (gomock "github.com/golang/mock/gomock"reflect "reflect"
)// MockDoer is a mock of Doer interface.
type MockDoer struct {ctrl     *gomock.Controllerrecorder *MockDoerMockRecorder
}// MockDoerMockRecorder is the mock recorder for MockDoer.
type MockDoerMockRecorder struct {mock *MockDoer
}// NewMockDoer creates a new mock instance.
func NewMockDoer(ctrl *gomock.Controller) *MockDoer {mock := &MockDoer{ctrl: ctrl}mock.recorder = &MockDoerMockRecorder{mock}return mock
}// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockDoer) EXPECT() *MockDoerMockRecorder {return m.recorder
}// DoSomething mocks base method.
func (m *MockDoer) DoSomething(arg0 int, arg1 string) error {m.ctrl.T.Helper()ret := m.ctrl.Call(m, "DoSomething", arg0, arg1)ret0, _ := ret[0].(error)return ret0
}// DoSomething indicates an expected call of DoSomething.
func (mr *MockDoerMockRecorder) DoSomething(arg0, arg1 interface{}) *gomock.Call {mr.mock.ctrl.T.Helper()return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DoSomething", reflect.TypeOf((*MockDoer)(nil).DoSomething), arg0, arg1)
}// SaySomething mocks base method.
func (m *MockDoer) SaySomething(arg0 string) string {m.ctrl.T.Helper()ret := m.ctrl.Call(m, "SaySomething", arg0)ret0, _ := ret[0].(string)return ret0
}// SaySomething indicates an expected call of SaySomething.
func (mr *MockDoerMockRecorder) SaySomething(arg0 interface{}) *gomock.Call {mr.mock.ctrl.T.Helper()return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaySomething", reflect.TypeOf((*MockDoer)(nil).SaySomething), arg0)
}

接下来,我们在测试中定义一个模拟控制器。一个模拟控制器负责跟踪和断言其相关模拟对象的期望。我们可以通过传递一个*testing.T类型的值给它的构造函数来获得一个模拟控制器,并使用它来构造一个Doer接口的模拟对象。

//core of gomock
mockCtrl := gomock.NewController(t)
//used to trigger final assertions. if its ignored, mocking assertions will never fail
defer mockCtrl.Finish()
// create a new mock object, passing the controller instance as parameter
// for a newly created mock object it will accept any input and outpuite
// need to define its behavior with the method expect
mockDoer := mocks.NewMockDoer(mockCtrl)

使用参数匹配器

在GoMock中,一个参数可以被期望有一个固定的值,也可以被期望与一个谓词(称为匹配器)相匹配。匹配器用于表示被模拟方法的预期参数范围。下列匹配器在Gomock中被预先定义了:

  • gomock.Any() : 匹配任何值(任何类型)。
  • gomock.Eq(x) : 使用反射来匹配与x深度相等的值。
  • gomock.Nil() : 匹配nil

user/user_test.go

package userimport ("fmt""github.com/golang/mock/gomock""gomock_test/mocks""testing"
)func TestUse(t *testing.T) {//core of gomockmockCtrl := gomock.NewController(t)//used to trigger final assertions. if its ignored, mocking assertions will never faildefer mockCtrl.Finish()// create a new mock object, passing the controller instance as parameter// for a newly created mock object it will accept any input and outpuite// need to define its behavior with the method expectmockDoer := mocks.NewMockDoer(mockCtrl)testUser := &User{Doer: mockDoer}//// Expect Do to be called once with 123 and "Hello GoMock" as parameters, and return nil from the mocked call.mockDoer.EXPECT().DoSomething(123, "Hello GoMock").Return(nil).Times(1)fmt.Println(testUser.Use())fmt.Println()
}func TestUser_SaySomething(t *testing.T) {mockCtrl := gomock.NewController(t)defer mockCtrl.Finish()mockDoer := mocks.NewMockDoer(mockCtrl)user := User{Doer: mockDoer,}type args struct {num int}tests := []struct {name    stringargs    argswant    stringexpect  func()wantErr bool}{{name: "Positive test case 1",expect: func() {mockDoer.EXPECT().SaySomething("spam").Return("bad")},args:    args{num: 3},wantErr: false,want:    "bad",},}for _, tt := range tests {t.Run(tt.name, func(t *testing.T) {tt.expect()if got := user.SaySomething(tt.args.num); (got != tt.want) != tt.wantErr {fmt.Println("gott :", got)t.Errorf("User.SaySomething() = %v, want %v", got, tt.want)}})}
}

而单元测试的结果将是这样的:

=== RUN   TestUser_SaySomething
=== RUN   TestUser_SaySomething/Positive_test_case_1
--- PASS: TestUser_SaySomething (0.00s)--- PASS: TestUser_SaySomething/Positive_test_case_1 (0.00s)
PASS
ok      github.com/tokopedia/go_learning/udemy/pzn/gomock_test/user     1.100s

相关文章:

go语言中的GoMock

GoMock是一个Go框架。它与内置的测试包整合得很好,并在单元测试时提供了灵活性。正如我们所知,对具有外部资源(数据库、网络和文件)或依赖关系的代码进行单元测试总是很麻烦。 安装 为了使用GoMock,我们需要安装gomo…...

DIFFWAVE: A VERSATILE DIFFUSION MODEL FOR AUDIO SYNTHESIS (Paper reading)

DIFFWAVE: A VERSATILE DIFFUSION MODEL FOR AUDIO SYNTHESIS Zhifeng Kong, Computer Science and Engineering, UCSD, ICLR2021, Code, Paper 1. 前言 在这项工作中,我们提出了DiffWave,这是一种用于条件和无条件波形生成的多功能扩散概率模型。该模…...

排序算法8----归并排序(非递归)(C)

1、介绍 归并排序既可以是内排序(在内存上的数据排序),也可以是外排序(磁盘上)(硬盘)(在文件中的数据排序)。 其他排序一般都是内排序。 区别于快速排序的非递归&#xf…...

Golang 里的 context

context 的作用 go 的编程中,常常会在一个 goroutine 中启动多个 goroutine,然后有可能在这些 goroutine 中又启动多个 goroutine。 如上图,在 main 函数中,启动了一个 goroutine A 和 goroutine B,然后 goroutine A …...

PHP短链接url还原成长链接

在开发过程中,碰到了需要校验用户回填的短链接是不是系统所需要的,于是就需要还原找出短链接所对应的长链接。 长链接转短链接 在百度上搜索程序员,跳转页面后的url就是一个长链接。当然你可以从任何地方复制一个长链接过来。 长链接 http…...

redis原理(三)redis命令

一、字符串命令: 1、字符串基本操作: 2、自增自减 :如果一个值可以被解释为十进制整数或者浮点数,redis允许用户对这个字符串进行INCR*、DECR*操作。 (1)INCR key:将键存储的值的值加1。 &a…...

教程:在Django中实现微信授权登录

教程:在Django中实现微信授权登录 本教程将引导您如何在Django项目中实现微信授权登录。在本教程中,我们将使用自定义的用户模型User,并通过微信提供的API来进行用户认证。 在进行以下教程之前,请确保你已经在微信开放平台添加了…...

YOLOv5改进 | 主干篇 | 12月份最新成果TransNeXt特征提取网络(全网首发)

一、本文介绍 本文给大家带来的改进机制是TransNeXt特征提取网络,其发表于2023年的12月份是一个最新最前沿的网络模型,将其应用在我们的特征提取网络来提取特征,同时本文给大家解决其自带的一个报错,通过结合聚合的像素聚焦注意力和卷积GLU&…...

【java八股文】之计算机网络系列篇

1、TCP/IP和UDP模型 TCP/IP分层(4层):应用层,传输层,网络层,数据链路层 网络的七层架构 (7层):应用层,表示层,会话层,传输层&#xff…...

SpringAMQP的使用

1. 简介: SpringAMQP是基于RabbitMQ封装的一套模板,并且还利用SpringBoot对其实现了自动装配,使用起来非常方便。 SpringAmqp的官方地址:https://spring.io/projects/spring-amqp SpringAMQP提供了三个功能: 自动声…...

MATLAB - 使用运动学 DH 参数构建机械臂

系列文章目录 前言 一、 使用 Puma560 机械手机器人的 Denavit-Hartenberg (DH) 参数,逐步建立刚体树形机器人模型。在连接每个关节时,指定其相对 DH 参数。可视化机器人坐标系,并与最终模型进行交互。 DH 参数定义了每个刚体通过关节与其父…...

2024年腾讯云新用户优惠云服务器价格多少?

腾讯云服务器租用价格表:轻量应用服务器2核2G3M价格62元一年、2核2G4M价格118元一年,540元三年、2核4G5M带宽218元一年,2核4G5M带宽756元三年、轻量4核8G12M服务器446元一年、646元15个月,云服务器CVM S5实例2核2G配置280.8元一年…...

如何在原型中实现继承和多态

在JavaScript中,我们可以通过原型链来实现继承。以下是如何在原型中实现继承的例子: // 定义一个动物原型 var Animal function() {}; Animal.prototype.move function() { console.log(‘This animal can move.’); }; // 定义一个狗的原型&#xf…...

MySQL/Oracle 的 字符串拼接

目录 MySQL、Oracle 的 字符串拼接1、MySQL 的字符串拼接1.1 CONCAT(str1,str2,...) : 可以拼接多个字符串1.2 CONCAT_WS(separator,str1,str2,...) : 指定分隔符拼接多个字符串1.3 GROUP_CONCAT(expr) : 聚合函数,用于将多行的值连接成一个字符串。 2、Oracle 的字…...

【Java SE语法篇】10.String类

📚博客主页:爱敲代码的小杨. ✨专栏:《Java SE语法》 ❤️感谢大家点赞👍🏻收藏⭐评论✍🏻,您的三连就是我持续更新的动力❤️ 文章目录 前言1. String类1.1 字符串的构造1.2 String对象的比…...

【Python】数据可视化--基于TMDB_5000_Movie数据集

一、数据准备 tmdb_5000_movie数据集下载 二、数据预处理 观察数据集合情况 import pandas as pd import ast import warnings warnings.filterwarnings(ignore) # 加载数据集 df pd.read_csv(tmdb_5000_movies.csv) # 查看数据集信息 print(df.info()) 由于原数据集包含的…...

学习Vue的插槽总结

今天学习了Vue的插槽,在这之前学习使用组件的使用还没有试过在父组件中给子组件插入html结构,今天学习的插槽正是拿来实现这一功能的,这也是一种组件中通信的方式,首先插槽分为三类:默认插槽、具名插槽、作用域插槽。接…...

第九篇 API设计原则与最佳实践

深入浅出HTTP请求前后端交互系列专题 第一章 引言-HTTP协议基础概念和前后端分离架构请求交互概述 第二章 HTTP请求方法、状态码详解与缓存机制解析 第三章 前端发起HTTP请求 第四章 前后端数据交换格式详解 第五章 跨域资源共享(CORS):现代W…...

新版AndroidStudio配置maven阿里云镜像

project下的build.gradle: // Top-level build file where you can add configuration options common to all sub-projects/modules. // 注意jdk版本需要17以上,因为8.1.3的gradle需要jdk17以上 //plugins { // id com.android.application version…...

【OSG案例详细分析与讲解】之十一:【多效果的3D动画】

目录 ​​​​​​​一、【多效果的3D动画】前言 二、【多效果的3D动画】实现效果...

Java 语言特性(面试系列2)

一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...