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、介绍 归并排序既可以是内排序(在内存上的数据排序),也可以是外排序(磁盘上)(硬盘)(在文件中的数据排序)。 其他排序一般都是内排序。 区别于快速排序的非递归…...

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层):应用层,表示层,会话层,传输层ÿ…...

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.’); }; // 定义一个狗的原型…...

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动画】实现效果...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...

2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...

Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...