当前位置: 首页 > 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动画】实现效果...

XML Group端口详解

在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...

HTML 语义化

目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案&#xff1a; 语义化标签&#xff1a; <header>&#xff1a;页头<nav>&#xff1a;导航<main>&#xff1a;主要内容<article>&#x…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...

站群服务器的应用场景都有哪些?

站群服务器主要是为了多个网站的托管和管理所设计的&#xff0c;可以通过集中管理和高效资源的分配&#xff0c;来支持多个独立的网站同时运行&#xff0c;让每一个网站都可以分配到独立的IP地址&#xff0c;避免出现IP关联的风险&#xff0c;用户还可以通过控制面板进行管理功…...

Windows安装Miniconda

一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...