使用 gomonkey Mock 函数及方法示例详解

目录
  • 前言
  • 函数
  • 方法
  • 参考

前言

在 Golang 语言中,写单元测试的时候,不可避免的会涉及到对其他函数及方法的 Mock,即在假设其他函数及方法响应预期结果的同时,校验被测函数的响应是否符合预期。

其中,在 Mock 其他函数及方法的时候,我们常用到的一个测试类库是「gomonkey」。特别地,对于方法和函数的 Mock,略有差异,在这里我们就分别给出函数和方法 Mock 示例,方便大家参考。

函数

在 Golang 语言中,函数是没有接受者的方法,其形式为

func function_name([parameter list]) [return_types] {
   函数体
}

对于函数的 Mock 相对来说比较简单,假设我们对 A 函数进行单元测试,且 A 函数里面又调用了 B 函数,例如

func A(ctx context.Context, str string) error {
   if len(str) == 0 {
	  return errors.New("str is empty")
   }
   return test_package_name.B(ctx, str)
}

为了将 A 函数的每一行代码都覆盖到,则其单元测试可以写为:

func TestA(t *testing.T) {
	type args struct {
		ctx    context.Context
		str    string
	}
	tests := []struct {
		name    string
		args    args
		Setup   func(t *testing.T)
		wantErr error
	}{
		{
			name: "len(str) == 0",
			wantErr: errors.New("str is empty")
		},
		{
			name: "正常响应",
			Setup: func(t *testing.T) {
				patches := gomonkey.ApplyFunc(test_package_name.B, func(_ context.Context, _ string) error {
					return nil
				})
				t.Cleanup(func() {
					patches.Reset()
				})
			},
			args: args{
				ctx:     context.Background(),
				str:     "test",
			},
			wantErr: nil,
		},
	}

	// 执行测试用例
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if tt.Setup != nil {
				tt.Setup(t)
			}
			err := A(tt.args.ctx, tt.args.str)
			if err != nil {
				assert.EqualError(t, err, tt.wantErr.Error(), "error 不符合预期")
			}
		})
	}
}

其中,ApplyFunc函数是用来 Mock 函数的,其第一个参数为需要 Mock 的函数名称(不需要写参数列表),第二个参数为需要 Mock 的函数结果;特别地,在Setup里面,我们要记得显式调用Cleanuppatches进行Reset操作,防止该 Mock 影响其他测试用例。

方法

在 Golang 语言中,方法是含有接受者的函数,其形式为

func (variable_name variable_data_type) function_name([parameter list]) [return_type]{
   函数体
}

对于方法的 Mock 相对来说复杂一下,假设我们对 A 函数进行单元测试,且 A 函数里面又调用了结构 C 的 B 方法,例如

func A(ctx context.Context, str string) error {
   if len(str) == 0 {
	  return errors.New("str is empty")
   }
   c := &test_package_name.C{}
   return c.B(ctx, str)
}

为了将 A 函数的每一行代码都覆盖到,则其单元测试可以写为:

func TestA(t *testing.T) {
	// 初始化C结构
	var c *test_package_name.C

	type args struct {
		ctx    context.Context
		str    string
	}
	tests := []struct {
		name    string
		args    args
		Setup   func(t *testing.T)
		wantErr error
	}{
		{
			name: "len(str) == 0",
			wantErr: errors.New("str is empty")
		},
		{
			name: "正常响应",
			Setup: func(t *testing.T) {
				patches := gomonkey.ApplyMethod(reflect.TypeOf(c), "B", func(_ *test_package_name.C, _ context.Context, _ string) error {
					return nil
				})
				t.Cleanup(func() {
					patches.Reset()
				})
			},
			args: args{
				ctx:     context.Background(),
				str:     "test",
			},
			wantErr: nil,
		},
	}

	// 执行测试用例
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if tt.Setup != nil {
				tt.Setup(t)
			}
			err := A(tt.args.ctx, tt.args.str)
			if err != nil {
				assert.EqualError(t, err, tt.wantErr.Error(), "error 不符合预期")
			}
		})
	}
}

其中,ApplyMethod函数是用来 Mock 方法的,其第一个参数为需要 Mock 的方法的接受者类型,第二个参数为需要 Mock 的方法名称(字符串类型),第三个参数为需要 Mock 的方法的定义及 Mock 结果;特别地,第一个参数和第三个参数需要我们注意:

  • 第一个参数,需要使用reflect.TypeOf获取接受者的类型,初始化的接受者必须是真正的类型,如结构 C 组合了结构 D,而B方法是通过组合 D 得到的,则初始化的时候需要定义结构 D,而不是结构 C,否则会报空指针异常;
  • 第三个参数,虽然B方法的声明是func(ctx context.Context, str string),但是在使用ApplyMethod的时候,需要将B方法的声明修改为func(c *test_package_name.C, ctx context.Context, str string),即需要将方法的接受者置为方法的第一个参数。

参考

还有就是,大家在使用gomonkey的时候,有可能遇到权限校验的问题以及非 Debug 模式运行失败的问题,可以参考:

golang使用gomonkey和monkey来mock方法或者函数时报panic: permission denied

使用 gomonkey 遇到非 debug 模式执行失败的问题及解决方法

到这里,本文就要结束了,希望对大家有所帮助。

到此这篇关于使用 gomonkey Mock 函数及方法的文章就介绍到这了,更多相关gomonkey Mock 函数内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2022-05-31

使用Gomock进行单元测试的方法示例

在开发过程中往往需要配合单元测试,但是很多时候,单元测试需要依赖一些比较复杂的准备工作,比如需要依赖数据库环境,需要依赖网络环境,单元测试就变成了一件非常麻烦的事情.举例来说,比如我们需要请求一个网页,并将请求回来的数据进行处理.在刚开始的时候,我通常都会先启动一个简单的http服务,然后再运行我的单元测试.可是这个单元测试测起来似乎非常笨重.甚至在持续集成过程中,我还为了能够自动化测试,特意写了一个脚本自动启动相应的服务.事情似乎需要进行一些改变. mock对象就是为了解决上面的问题而诞生的,

Go语言Mock使用基本指南详解

当前的实践中问题 在项目之间依赖的时候我们往往可以通过mock一个接口的实现,以一种比较简洁.独立的方式,来进行测试.但是在mock使用的过程中,因为大家的风格不统一,而且很多使用minimal implement的方式来进行mock,这就导致了通过mock出的实现各个函数的返回值往往是静态的,就无法让caller根据返回值进行的一些复杂逻辑. 首先来举一个例子 package task type Task interface { Do(int) (string, error) } 通过mini

用gomock进行mock测试的方法示例

在开发过程中往往需要配合单元测试,但是很多时候,单元测试需要依赖一些比较复杂的准备工作,比如需要依赖数据库环境,需要依赖网络环境,单元测试就变成了一件非常麻烦的事情.举例来说,比如我们需要请求一个网页,并将请求回来的数据进行处理.在刚开始的时候,我通常都会先启动一个简单的http服务,然后再运行我的单元测试.可是这个单元测试测起来似乎非常笨重.甚至在持续集成过程中,我还为了能够自动化测试,特意写了一个脚本自动启动相应的服务.事情似乎需要进行一些改变. mock对象就是为了解决上面的问题而诞生的,

Python 中如何实现参数化测试的方法示例

之前,我曾转过一个单元测试框架系列的文章,里面介绍了 unittest.nose/nose2 与 pytest 这三个最受人欢迎的 Python 测试框架. 本文想针对测试中一种很常见的测试场景,即参数化测试,继续聊聊关于测试的话题,并尝试将这几个测试框架串联起来,做一个横向的比对,加深理解. 1.什么是参数化测试? 对于普通测试来说,一个测试方法只需要运行一遍,而参数化测试对于一个测试方法,可能需要传入一系列参数,然后进行多次测试. 比如,我们要测试某个系统的登录功能,就可能要分别传入不同的用

python mock测试的示例

mock总所周知是模拟的意思,我们在做接口测试的时候有时候会发现部分功能依赖其他业务场景或者第三方功能或者是线上数据或者业务场景过于复杂(需要大量调用)的情况,没有办法通过接口调用或者做断言,这个时候就需要mock了 python2中需要pip install mock来导入mock第三方模块,而python3中被引入到unittest框架中,直接from unittest import mock 就可以导入mock模块了 在单元测试中只针对当前单元做测试,就是测试当前方法或者是当前类,但是如果

Python分割训练集和测试集的方法示例

数据集介绍 使用数据集Wine,来自UCI  .包括178条样本,13个特征. import pandas as pd import numpy as np df_wine = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data', header=None) df_wine.columns = ['Class label', 'Alcohol', 'Malic acid', '

详解Spring MVC如何测试Controller(使用springmvc mock测试)

在springmvc中一般的测试用例都是测试service层,今天我来演示下如何使用springmvc mock直接测试controller层代码. 1.什么是mock测试? mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法. 2.为什么要使用mock测试? 使用Mock O bject进行测试,主要是用来模拟那些在应用中不容易构造(如HttpServletRequest必须在Servlet容器中才能构造出来)或者比较复杂的对象(如J

在vue中使用express-mock搭建mock服务的方法

首先安装 nodemon ,如果是全局安装,那么所有的项目都可以使用mock服务 npm install nodemon 再安装express-mockjs npm i -D express-mockjs 接下来按照以下的步骤来 第一步 在项目根目录下建立api-interface文件,再建立一个文件夹叫mocks,这里面放json或者js都可以,然后再在mocks同级目录下建立app.js文件 第二步编写app.js 第三部 在mocks文件中编写一个叫test的json文件,文件中代码如下

oracle中decode函数的使用方法示例

decode的几种用法 1:使用decode判断字符串是否一样 DECODE(value,if1,then1,if2,then2,if3,then3,...,else) 含义为 IF 条件=值1 THEN RETURN(value 1) ELSIF 条件=值2 THEN RETURN(value 2) ...... ELSIF 条件=值n THEN RETURN(value 3) ELSE RETURN(default) END IF sql测试 select empno,decode(empn

Linux中在防火墙中开启80端口方法示例

linux如果刚安装好防火墙时我们常用的端口是没有增加的,也就是说不能访问,那么要怎么把常用端口增加到防火墙通过状态呢,下面我们以80端口为例子吧. 最近自己在学习Linux.搭建一个LNMP环境.在测试时一切都好.然后重启Linux后.再次访问网站无法打开.最终原因是在防火墙中没有加入 80 端口的规则.具体方法如下: 在CentOS下配置iptables防火墙,是非常必要的.来我们学习如何配置!,其它版本一下: 1.打开iptables的配置文件: 代码如下 vi /etc/sysconfi

Java使用代理进行网络连接方法示例

需求是这样的: 一.界面上要有这样几种代理类型可以选. 1.HTTP代理 2.Socks代理 3.不使用代理(直连) 4.使用浏览器设置(浏览器也是HTTP.Socks.直连三种). 可参考QQ登录设置里的代理能,其实跟qq的代理功能是一样的. 二.测试使用所填写的代理配置信息是否可连接 三.记录用户上次选择的代理配置,默认使用用户上次使用的代理配置进行网络连接. 程序运行环境是WindowsXP.Windows7.Windows8系统. 使用的技术为Java7,Swing,CXF. 难点: 1

Spring框架依赖注入方法示例

在阅读这篇文章之前,大家可以先参阅<理解Spring中的依赖注入和控制反转>一文,了解下依赖注入和控制反转的相关内容. 三种依赖注入的方式 属性注入,通过setter方法注入bean的属性值或依赖的对象 构造注入 工厂方法注入(很少使用) 例子 这里我们使用了spring-4.3.2,maven配置文件 <dependency> <groupid>org.springframework</groupid> spring-core</artifactid