Go基础学习11-测试工具gomock和monkey的使用

文章目录

  • 基础回顾
  • Mock
    • Mock是什么
    • 安装gomock
    • Mock使用
      • 1. 创建user.go源文件
      • 2. 使用mockgen生成对应的Mock文件
      • 3. 使用mockgen命令生成后在对应包mock下可以查看生成的mock文件
      • 4. 编写测试代码
      • 5. 运行代码并查看输出
  • Gomonkey
    • Gomonkey优势
    • 安装
    • 使用
      • 对函数进行monkey
      • 对结构体中方法进行monkey
      • 对全局变量进行monkey

基础回顾

测试:
在这里插入图片描述在这里插入图片描述
● 建立完善的回归测试,如果采用微服务,可以限制测试范围,只需要保证独立的微服务内部功能正常,因为微服务对外暴露的是接口。
● 为什么要将测试文件和源文件写到一个包中:便于测试包级别的示例,方法。

  • go test 命令是一个按照一定约定和组织的测试代码驱动测试,在包目录下 所有以_test.go结尾的文件都会被视为测试文件。并且 _test.go结尾的文件不会被go build 编译到可执行文件中。

Mock

Mock是什么

Mock是单元测试中常见的一种技术,就是在测试过程中,对于一些不容易构造或者获取的对象,创建一个Mock对象来模拟对象的行为,从而把测试与测试边界以外的对象隔离开。
优点:
团队并行工作
测试驱动开发 TDD (Test-Driven Development)
测试覆盖率
隔离系统
缺点

  • Mock不是万能的,使用Mock也存在着风险,需要根据项目实际情况和具体需要来确定是否选用Mock。
  • 测试过程中如果大量使用Mock,mock测试的场景失去了真实性,可能会导致在后续的系统性测试时才发现bug,使得缺陷发现的较晚,可能会造成后续修复成本更大

Mock广泛应用于接口类的方法的生成。 针对接口可以使用mock,针对不是接口的函数或者变量使用下面的monkey。

安装gomock

go get github.com/golang/mock/gomock
go get github.com/golang/mock/mockgen
go install github.com/golang/mock/mockgen@v1.6.0

安装完成后执行mockgen命令查看是否生效

Mock使用

1. 创建user.go源文件

// Package mock  ------------------------------------------------------------
// @file      : user.go
// @author    : WeiTao
// @contact   : 15537588047@163.com
// @time      : 2024/10/4 13:16
// ------------------------------------------------------------
package mock

import "context"

type User struct {
	Mobile   string
	Password string
	NickName string
}

type UserServer struct {
	Db UserData
}

func (us *UserServer) GetUserByMobile(ctx context.Context, mobile string) (User, error) {
	user, err := us.Db.GetUserByMobile(ctx, mobile)
	if err != nil {
		return User{}, err
	}
	if user.NickName == "TestUser" {
		user.NickName = "TestUserModified"
	}
	return user, nil
}

type UserData interface {
	GetUserByMobile(ctx context.Context, mobile string) (User, error)
}

上述代码中的UserData是一个Interface{}类型,后面使用Mock生成的对象就是此接口对象,生成后可以将UserData接口中的方法GetUserByMoblie方法实现黑盒,从而无需关注具体实现细节,直接可以设置此函数对应的返回值。

2. 使用mockgen生成对应的Mock文件

mockgen使用:

// 源码模式
  mockgen -source 需要mock的文件名 -destination 生成的mock文件名 -package 生成mock文件的包名
// 参考示例:
mockgen -source user.go -destination=./mock/user_mock.go -package=mock

3. 使用mockgen命令生成后在对应包mock下可以查看生成的mock文件

// Code generated by MockGen. DO NOT EDIT.
// Source: user.go

// Package mock is a generated GoMock package.
package mock

import (
	context "context"
	reflect "reflect"

	gomock "github.com/golang/mock/gomock"
)

// MockUserData is a mock of UserData interface.
type MockUserData struct {
	ctrl     *gomock.Controller
	recorder *MockUserDataMockRecorder
}

// MockUserDataMockRecorder is the mock recorder for MockUserData.
type MockUserDataMockRecorder struct {
	mock *MockUserData
}

// NewMockUserData creates a new mock instance.
func NewMockUserData(ctrl *gomock.Controller) *MockUserData {
	mock := &MockUserData{ctrl: ctrl}
	mock.recorder = &MockUserDataMockRecorder{mock}
	return mock
}

// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockUserData) EXPECT() *MockUserDataMockRecorder {
	return m.recorder
}

// GetUserByMobile mocks base method.
func (m *MockUserData) GetUserByMobile(ctx context.Context, mobile string) (User, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "GetUserByMobile", ctx, mobile)
	ret0, _ := ret[0].(User)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// GetUserByMobile indicates an expected call of GetUserByMobile.
func (mr *MockUserDataMockRecorder) GetUserByMobile(ctx, mobile interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByMobile", reflect.TypeOf((*MockUserData)(nil).GetUserByMobile), ctx, mobile)
}

观察上述Mock文件可以看到实现了GetUserByMobile方法等。

4. 编写测试代码

// ------------------------------------------------------------
// package test
// @file      : user_test.go
// @author    : WeiTao
// @contact   : 15537588047@163.com
// @time      : 2024/10/4 13:23
// ------------------------------------------------------------
package mock

import (
	"context"
	"github.com/golang/mock/gomock"
	"testing"
)

func TestGetUserByMobile(t *testing.T) {
	// mock准备工作
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	userData := NewMockUserData(ctl)
	userData.EXPECT().GetUserByMobile(gomock.Any(), "15023076751").Return(
		User{
			Mobile:   "15023076751",
			Password: "123456",
			NickName: "TestUser",
		},
		nil,
	)
	// 实际调用过程
	userServer := UserServer{
		Db: userData,
	}
	user, err := userServer.GetUserByMobile(context.Background(), "15023076751")
	// 判断过程
	if err != nil {
		t.Error("GetUserByMobile error:", err)
	}
	if user.Mobile != "15023076751" || user.Password != "123456" || user.NickName != "TestUserModified" {
		t.Error("GetUserByMobile result is not expected.")
	}
}

5. 运行代码并查看输出

GOROOT=/usr/local/go #gosetup
GOPATH=/home/wt/Backend/go/goprojects #gosetup
/usr/local/go/bin/go test -c -o /home/wt/.cache/JetBrains/GoLand2024.2/tmp/GoLand/___TestGetUserByMobile_in_golearndetail_test_mock.test golearndetail/test/mock #gosetup
/usr/local/go/bin/go tool test2json -t /home/wt/.cache/JetBrains/GoLand2024.2/tmp/GoLand/___TestGetUserByMobile_in_golearndetail_test_mock.test -test.v=test2json -test.paniconexit0 -test.run ^\QTestGetUserByMobile\E$
=== RUN   TestGetUserByMobile
--- PASS: TestGetUserByMobile (0.00s)
PASS

Process finished with the exit code 0

Gomonkey

Gomonkey优势

上面使用mockgen生成对应的mock文件缺点非常明显,只能对于接口类的函数进行mock,然而实际项目并非所有函数都是接口类函数,大部分是内部使用的临时函数以及变量等,此时对这些函数以及变量无法使用mockgen生成对应的mock文件,此时可以使用另一个工具gomonkey
链接:https://github.com/agiledragon/gomonkey

安装

参考官网安装说明:

$ go get github.com/agiledragon/gomonkey/v2@v2.11.0

使用

对函数进行monkey

  1. 编写函数
package monkey

func networkCompute(a, b int) (int, error) {
	c := a + b
	return c, nil
}

func compute(a, b int) (int, error) {
	c, err := networkCompute(a, b)
	return c, err
}
  1. 编写测试用例
// 对函数进行mock
func Test_compute(t *testing.T) {
	patches := gomonkey.ApplyFunc(networkCompute, func(a, b int) (int, error) {
		return 8, nil
	})
	defer patches.Reset()
	sum, err := compute(1, 2)
	if err != nil {
		t.Error("Error occurred:", err)
	}
	fmt.Printf("sum : %d\n", sum)
	if sum != 8 {
		t.Error("Error occurred in sum:", err)
	}
}

在使用gomonkey运行测试用例的时候,直接run会报内联错误,解决方法有两个:

  • 在终端执行命令go test时加上参数:go test -gcflags=all=-l
  • 在Goland编辑器添加对应go运行变量:-gcflags=all=-l
  • 在这里插入图片描述
    加上 "all=-N -l"和”=all=-l"效果相同。
  1. 运行结果
/usr/local/go/bin/go tool test2json -t /home/wt/.cache/JetBrains/GoLand2024.2/tmp/GoLand/___7Test_compute_in_golearndetail_test_monkey.test -test.v=test2json -test.paniconexit0 -test.run ^\QTest_compute\E$
=== RUN   Test_compute
sum : 8
--- PASS: Test_compute (0.00s)
PASS

Process finished with the exit code 0

对结构体中方法进行monkey

  1. 编写代码
type Compute struct{}

func (c *Compute) NetworkCompute(a, b int) (int, error) {
	sum := a + b
	return sum, nil
}

func (c *Compute) Compute(a, b int) (int, error) {
	sum, err := c.NetworkCompute(a, b)
	return sum, err
}
  1. 编写测试用例
// 对结构体中的方法进行mock
func Test_Compute_NetworkCompute(t *testing.T) {
	var compute *Compute
	patches := gomonkey.ApplyMethod(reflect.TypeOf(compute), "NetworkCompute", func(_ *Compute, a, b int) (int, error) {
		return 10, nil
	})
	defer patches.Reset()
	compute = &Compute{}
	sum, err := compute.Compute(3, 2)
	if err != nil {
		t.Error("Error occurred:", err)
	}
	fmt.Printf("sum : %d\n", sum)
	if sum != 10 {
		t.Error("Error occurred in sum:", err)
	}
}
  1. 运行结果
/usr/local/go/bin/go tool test2json -t /home/wt/.cache/JetBrains/GoLand2024.2/tmp/GoLand/___2Test_Compute_NetworkCompute_in_golearndetail_test_monkey.test -test.v=test2json -test.paniconexit0 -test.run ^\QTest_Compute_NetworkCompute\E$
=== RUN   Test_Compute_NetworkCompute
sum : 10
--- PASS: Test_Compute_NetworkCompute (0.00s)
PASS

Process finished with the exit code 0

对全局变量进行monkey

代码展示:

var num = 5

// 对变量进行mock
func Test_GlobalVal(t *testing.T) {
	patches := gomonkey.ApplyGlobalVar(&num, 10)
	defer patches.Reset()
	if num != 10 {
		t.Error("Error occurred in num:mock failure", num)
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android SELinux——安全策略(三)

SELinux 通过严格的访问控制机制增强了 Linux 系统的安全性。它通过标签和安全策略来控制进程和文件的访问权限,从而保护系统免受未经授权的访问和攻击。 一、策略介绍 1、主要组件 安全标签(Security Labels):每个文件、目录、…

vite学习教程02、vite+vue2配置环境变量

文章目录 前言1、安装依赖2、配置环境变量3、应用环境变量4、运行和构建项目资料获取 前言 博主介绍:✌目前全网粉丝3W,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java后端技术领域。 涵盖技术内容&#xff1…

Python的pandas库基本操作(数据分析)

一、安装,导入 1、安装 使用包管理器安装: pip3 install pandas 2、导入 import pandas as pd as是为了方便引用起的别名 二、DateFrame 在Pandas库中,DataFrame 是一种非常重要的数据结构,它提供了一种灵活的方式来存储和…

typora笔记导出word格式:

Pandoc:各系统下载github链接 https://github.com/jgm/pandoc/releases/latest windows安装包 链接:https://pan.baidu.com/s/17AZNIMImbzFtWJAcAfAB0g?pwd55l2 提取码:55l2 先解压压缩包 点击 设置Pandoc路径,然后选择pa…

【Sceneform-EQR】(手势优化)通过手势事件实现在AR/VR等三维场景中的控制模型旋转、平移与缩放

在上一篇文档中,我们实现了通过手势控制模型节点的旋转、缩放和平移。现在本文将介绍如何优化上一篇做的手势控制器,从而实现更好的跟手效果。 相关链接:【Sceneform-EQR】(手势控制器实现)通过手势事件实现在AR/VR等…

FiBiNET模型实现推荐算法

1. 项目简介 A031-FiBiNET模型项目是一个基于深度学习的推荐系统算法实现,旨在提升推荐系统的性能和精度。该项目的背景源于当今互联网平台中,推荐算法在电商、社交、内容分发等领域的广泛应用。推荐系统通过分析用户的历史行为和兴趣偏好,预…

Linux平台Kafka高可用集群部署全攻略

🐇明明跟你说过:个人主页 🏅个人专栏:《大数据前沿:技术与应用并进》🏅 🔖行路有良友,便是天堂🔖 目录 一、引言 1、Kafka简介 2、Kafka核心优势 二、环境准备 1…

【学习笔记】SquareLine Studio安装教程(LVGL官方工具)

一.简介与导航: SquareLine Studio是由LVGL官方开发的一款UI设计工具,采用图形化进行界面UI设计,轻易上手。 SquareLine Studio官方网址:https://squareline.io/SquareLine Studio官方文档:https://docs.squareline.io…

上传本地项目到GitHub远程仓库(极简洁操作版)

第一步:在GitHub创建一个空的仓库 第二步:将仓库克隆(下载)到本地 第三步:将你要上传的所有文件放到这个克隆的仓库文件夹中 第四步:通过git add .将待上传文件添加到暂存区 此时,可以通过git …

css3-----2D转换、动画

2D 转换(transform) 转换(transform)是CSS3中具有颠覆性的特征之一,可以实现元素的位移、旋转、缩放等效果 移动:translate旋转:rotate缩放:scale 二维坐标系 2D 转换之移动 trans…

SpringBoot项目打成jar包,在其他项目中引用

1、首先新建一个SpringBoot工程 记得要将Gradle换成Maven 2、新建一个要引用的方法 3、打包的时候要注意: ① 不能使用springboot项目自带的打包插件进行打包,下面是自带的: ②要换成传统项目的maven打包,如下图: 依…

《网络安全自学教程》- Nmap使用及扫描原理分析

《网络安全自学教程》 Nmap(Network Mapper)是一款免费的开源网络扫描器,向目标主机发送特定的数据包,根据返回的流量特征,分析主机信息。主要功能有:「端口扫描」、「主机探测」、「服务识别」和「系统识别…

(接口测试)接口测试理论 http理论 接口测试流程 接口文档解析

一.接口测试理论 1.接口和接口测试 服务器为客户端开了一个验证接口(接口本质:函数方法)客户端向服务器传送的消息可以相当于函数的参数,接口是用来让客户端传递数据的 接口:相当于开了一个通道 当服务器要给客户端响…

笔记整理—linux进程部分(6)进程间通信、alarm和pause

两个进程间通信可能是任何两个进程间的通信(IPC)。同一个进程是在同一块地址空间中的,在不同的函数与文件以变量进程传递,也可通过形参传递。2个不同进程处于不同的地址空间,要互相通信有难度(内存隔离的原…

Awaken Likho恶意组织利用高级网络工具对俄罗斯政府发起“猛攻”

近日,俄罗斯政府机构和工业实体遭遇了一场名为“ Awaken Likho ”的网络活动攻击活动。 卡巴斯基表示,攻击者现在更倾向于使用合法MeshCentral平台的代理,而不是他们之前用来获得系统远程访问权限的UltraVNC模块。这家俄罗斯网络安全公司详细…

Golang | Leetcode Golang题解之第457题环形数组是否存在循环

题目: 题解: func circularArrayLoop(nums []int) bool {n : len(nums)next : func(cur int) int {return ((curnums[cur])%n n) % n // 保证返回值在 [0,n) 中}for i, num : range nums {if num 0 {continue}slow, fast : i, next(i)// 判断非零且方…

docker简述

1.安装dockers,配置docker软件仓库 安装,可能需要开代理,这里我提前使用了下好的包安装 启动docker systemctl enable --now docker查看是否安装成功 2.简单命令 拉取镜像,也可以提前下载使用以下命令上传 docker load -i imag…

大数据毕业设计选题推荐-B站热门视频数据分析-Python数据可视化-Hive-Hadoop-Spark

✨作者主页:IT研究室✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

使用C语言获取iostat中的await值的方法和方案

使用C语言获取iostat中的await值的方法和方案 1. 准备工作2. 调用iostat命令并获取输出3. 解析iostat输出4. 完整实现和错误处理5. 注意事项在Linux系统中,iostat命令是sysstat软件包的一部分,用于监控系统的CPU、网卡、tty设备、磁盘、CD-ROM等设备的活动情况和负载信息。其…

鸿蒙OS投票机制

(基于openharmony5.0) 投票机制 param get | grep ohos.boot.time 图 投票机制参数图 只有当所有的投票完成,开机动画才会退出,整理需要投票的系统应用(三方应用不参与投票)如下图所示: 以进程foundation为例&…