Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >使用 Gomock 进行单元测试

使用 Gomock 进行单元测试

作者头像
李海彬
发布于 2018-12-24 09:39:44
发布于 2018-12-24 09:39:44
3.6K00
代码可运行
举报
文章被收录于专栏:Golang语言社区Golang语言社区
运行总次数:0
代码可运行

在实际项目中,需要进行单元测试的时候。却往往发现有一大堆依赖项。这时候就是 Gomock 大显身手的时候了

Gomock 是 Go 语言的一个 mock 框架,官方的那种 ?

安装

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1$ go get -u github.com/golang/mock/gomock
2$ go install github.com/golang/mock/mockgen
  1. 第一步:我们将安装 gomock 第三方库和 mock 代码的生成工具 mockgen。而后者可以大大的节省我们的工作量。只需要了解其使用方式就可以
  2. 第二步:输入 mockgen 验证代码生成工具是否安装正确。若无法正常响应,请检查 bin 目录下是否包含该二进制文件

用法

mockgen 命令中,支持两种生成模式:

  1. source:从源文件生成 mock 接口(通过 -source 启用)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1mockgen -source=foo.go [other options]
  1. reflect:通过使用反射程序来生成 mock 接口。它通过传递两个非标志参数来启用:导入路径和逗号分隔的接口列表
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1mockgen database/sql/driver Conn,Driver

从本质上来讲,两种方式生成的 mock 代码并没有什么区别。因此选择合适的就可以了

写测试用例

在本文将模拟一个简单 Demo 来编写测试用例,熟悉整体的测试流程

步骤

  1. 想清楚整体逻辑
  2. 定义想要(模拟)依赖项的 interface(接口)
  3. 使用 mockgen 命令对所需 mock 的 interface 生成 mock 文件
  4. 编写单元测试的逻辑,在测试中使用 mock
  5. 进行单元测试的验证

目录

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1├── mock
2├── person
3│   └── male.go
4└── user
5    ├── user.go
6    └── user_test.go

编写

interface 方法

打开 person/male.go 文件,写入以下内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1package person
2
3type Male interface {
4    Get(id int64) error
5}
调用方法

打开 user/user.go 文件,写入以下内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1package user
 2
 3import "github.com/EDDYCJY/mockd/person"
 4
 5type User struct {
 6    Person person.Male
 7}
 8
 9func NewUser(p person.Male) *User {
10    return &User{Person: p}
11}
12
13func (u *User) GetUserInfo(id int64) error {
14    return u.Person.Get(id)
15}
生成 mock 文件

回到 mockd/ 的根目录下,执行以下命令

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1$ mockgen -source=./person/male.go -destination=./mock/male_mock.go -package=mock

在执行完毕后,可以发现 mock/ 目录下多出了 male_mock.go 文件,这就是 mock 文件。那么命令中的指令又分别有什么用呢?如下:

  • -source:设置需要模拟(mock)的接口文件
  • -destination:设置 mock 文件输出的地方,若不设置则打印到标准输出中
  • -package:设置 mock 文件的包名,若不设置则为 mock_ 前缀加上文件名(如本文的包名会为 mock_person)

想了解更多的指令符,可参见 官方文档

输出的 mock 文件
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1// Code generated by MockGen. DO NOT EDIT.
 2// Source: ./person/male.go
 3
 4// Package mock is a generated GoMock package.
 5package mock
 6
 7import (
 8    gomock "github.com/golang/mock/gomock"
 9    reflect "reflect"
10)
11
12// MockMale is a mock of Male interface
13type MockMale struct {
14    ctrl     *gomock.Controller
15    recorder *MockMaleMockRecorder
16}
17
18// MockMaleMockRecorder is the mock recorder for MockMale
19type MockMaleMockRecorder struct {
20    mock *MockMale
21}
22
23// NewMockMale creates a new mock instance
24func NewMockMale(ctrl *gomock.Controller) *MockMale {
25    mock := &MockMale{ctrl: ctrl}
26    mock.recorder = &MockMaleMockRecorder{mock}
27    return mock
28}
29
30// EXPECT returns an object that allows the caller to indicate expected use
31func (m *MockMale) EXPECT() *MockMaleMockRecorder {
32    return m.recorder
33}
34
35// Get mocks base method
36func (m *MockMale) Get(id int64) error {
37    ret := m.ctrl.Call(m, "Get", id)
38    ret0, _ := ret[0].(error)
39    return ret0
40}
41
42// Get indicates an expected call of Get
43func (mr *MockMaleMockRecorder) Get(id interface{}) *gomock.Call {
44    return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockMale)(nil).Get), id)
45}
测试用例

打开 user/user_test.go 文件,写入以下内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1package user
 2
 3import (
 4    "testing"
 5
 6    "github.com/EDDYCJY/mockd/mock"
 7
 8    "github.com/golang/mock/gomock"
 9)
10
11func TestUser_GetUserInfo(t *testing.T) {
12    ctl := gomock.NewController(t)
13    defer ctl.Finish()
14
15    var id int64 = 1
16    mockMale := mock.NewMockMale(ctl)
17    gomock.InOrder(
18        mockMale.EXPECT().Get(id).Return(nil),
19    )
20
21    user := NewUser(mockMale)
22    err := user.GetUserInfo(id)
23    if err != nil {
24        t.Errorf("user.GetUserInfo err: %v", err)
25    }
26}
27}
  1. gomock.NewController:返回 gomock.Controller,它代表 mock 生态系统中的顶级控件。定义了 mock 对象的范围、生命周期和期待值。另外它在多个 goroutine 中是安全的
  2. mock.NewMockMale:创建一个新的 mock 实例
  3. gomock.InOrder:声明给定的调用应按顺序进行(是对 gomock.After 的二次封装)
  4. mockMale.EXPECT().Get(id).Return(nil):这里有三个步骤,EXPECT()返回一个允许调用者设置期望和返回值的对象。Get(id) 是设置入参并调用 mock 实例中的方法。Return(nil) 是设置先前调用的方法出参。简单来说,就是设置入参并调用,最后设置返回值
  5. NewUser(mockMale):创建 User 实例,值得注意的是,在这里注入了 mock 对象,因此实际在随后的 user.GetUserInfo(id) 调用(入参:id 为 1)中。它调用的是我们事先模拟好的 mock 方法
  6. ctl.Finish():进行 mock 用例的期望值断言,一般会使用 defer 延迟执行,以防止我们忘记这一操作

测试

回到 mockd/ 的根目录下,执行以下命令

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1$ go test ./user
2ok      github.com/EDDYCJY/mockd/user

看到这样的结果,就大功告成啦!你可以自己调整一下 Return() 的返回值,以此得到不一样的测试结果哦

查看测试情况

测试覆盖率

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1$ go test -cover ./user
2ok      github.com/EDDYCJY/mockd/user   (cached)    coverage: 100.0% of statements

可通过设置 -cover 标志符来开启覆盖率的统计,展示内容为 coverage: 100.0%

可视化界面

  1. 生成测试覆盖率的 profile 文件
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1$ go test ./... -coverprofile=cover.out
  1. 利用 profile 文件生成可视化界面
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1$ go tool cover -html=cover.out
  1. 查看可视化界面,分析覆盖情况

更多

一、常用 mock 方法

调用方法
  • Call.Do():声明在匹配时要运行的操作
  • Call.DoAndReturn():声明在匹配调用时要运行的操作,并且模拟返回该函数的返回值
  • Call.MaxTimes():设置最大的调用次数为 n 次
  • Call.MinTimes():设置最小的调用次数为 n 次
  • Call.AnyTimes():允许调用次数为 0 次或更多次
  • Call.Times():设置调用次数为 n 次
参数匹配
  • gomock.Any():匹配任意值
  • gomock.Eq():通过反射匹配到指定的类型值,而不需要手动设置
  • gomock.Nil():返回 nil

建议更多的方法可参见 官方文档

二、生成多个 mock 文件

你可能会想一条条命令生成 mock 文件,岂不得崩溃?

当然,官方提供了更方便的方式,我们可以利用 go:generate 来完成批量处理的功能

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]
修改 interface 方法

打开 person/male.go 文件,修改为以下内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1package person
2
3//go:generate mockgen -destination=../mock/male_mock.go -package=mock github.com/EDDYCJY/mockd/person Male
4
5type Male interface {
6    Get(id int64) error
7}

我们关注到 go:generate 这条语句,可分为以下部分:

  1. 声明 //go:generate (注意不要留空格)
  2. 使用 mockgen 命令
  3. 定义 -destination
  4. 定义 -package
  5. 定义 source,此处为 person 的包路径
  6. 定义 interfaces,此处为 Male
重新生成 mock 文件

回到 mockd/ 的根目录下,执行以下命令

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1$ go generate ./...

再检查 mock/ 发现也已经正确生成了,在多个文件时是不是很方便呢 ?

总结

在单元测试这一环,gomock 给我们提供了极大的便利。能够 mock 掉许许多多的依赖项,其中还有很多的使用方式和功能。你可以 mark 住后详细阅读下官方文档,记忆会更深刻。


版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-12-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Golang语言社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【初识Go】| Day12 单元测试
我们说测试的时候一般是指自动化测试,也就是写一些小的程序用来检测被测试代码(产品代码)的行为和预期的一样,这些通常都是精心设计的执行某些特定的功能或者是通过随机性的输入待验证边界的处理。
yussuy
2020/12/25
3080
【初识Go】| Day12 单元测试
如何写出优雅的 Golang 代码
原文: https://draveness.me/golang-101.html
sunsky
2020/08/20
1.6K0
如何写出优雅的 Golang 代码
Golang 单元测试 - 逻辑层
前面我们完成了最麻烦的数据层的单元测试,今天我们来看看单元测试中最容易做的一层,数据逻辑层,也就是我们通常说的 service 或者 biz 等,是描述具体业务逻辑的地方,这一层包含我们业务最重要的逻辑。
LinkinStar
2023/02/22
5040
白话Golang单元测试
最近学习某个 Golang 单元测试的课程,发现其中推荐使用 gomonkey 这种黑科技,让人略感意外,毕竟在软件开发领域,诸如依赖注入之类的概念已经流传了几十年了,本文希望通过一个例子的演化过程,来总结出 Golang 单元测试的最佳实战。
LA0WAN9
2021/12/14
5080
go测试框架gomock的使用
gomock是golang官方开发维护的接口级别的mock方案,包含了GoMock包和mockgen工具两部分,其中GoMock包完成对桩对象生命周期的管理,mockgen工具用来生成interface对应的Mock类源文件。要使用gomock的一个前提是模块之间务必通过接口进行依赖,而不是依赖具体实现,否则mock会非常困难。这个工具目前业界用的并不多,主要是局限性太大,所以我们只需要简单了解一下如何使用就行。
Johns
2021/09/01
4.8K0
Go Mock 接口测试 单元测试 极简教程
To get the latest released version use: Go version < 1.16
一个会写诗的程序员
2022/05/30
2.1K0
Go Mock 接口测试 单元测试 极简教程
go 单元测试进阶篇
腾讯云数据库团队
2017/01/05
9K2
go 单元测试进阶篇
手把手教你如何进行 Golang 单元测试
点击上方蓝字,发现更多精彩 导语 本篇是对单元测试的一个总结,通过完整的单元测试手把手教学,能够让刚接触单元测试的开发者从整体上了解一个单元测试编写的全过程。最终通过两个问题,也能让写过单元测试的开发者收获单测执行时的一些底层细节知识。 引入 随着工程化开发在司内大力的推广,单元测试越来越受到广大开发者的重视。在学习的过程中,发现网上针对 Golang 单元测试大多从理论角度出发介绍,缺乏完整的实例说明,晦涩难懂的 API 让初学接触者难以下手。 本篇不准备大而全的谈论单元测试、笼统的介绍 Golang
腾讯VTeam技术团队
2021/06/02
1.5K0
Go 单元测试之mock接口测试
gomock 是一个 Go 语言的测试框架,在实际项目中,需要进行单元测试的时候。却往往发现有一大堆依赖项。这时候就是 Gomock 大显身手的时候了,用于编写单元测试时模拟和测试依赖于外部服务的代码。它允许你创建模拟对象(Mock Objects),这些对象可以预设期望的行为,以便在测试时模拟外部依赖,通常使用它对代码中的那些接口类型进行mock。
贾维斯Echo
2024/04/19
2230
Go 单元测试之mock接口测试
Go:微服务架构下的单元测试(基于 Ginkgo、gomock 、Gomega)
本文主要使用 Ginkgo[2] 、gomock[3] 、Gomega[4] 工具来实现单元测试,之前不了解的同学,可以先熟悉一下相关文档。
Freedom123
2024/03/29
5630
Go:微服务架构下的单元测试(基于 Ginkgo、gomock 、Gomega)
go test 测试用例那些事(二) mock
关于go的单元测试,之前有写过一篇帖子go test测试用例那些事,但是没有说go官方的库mock,很有必要单独说一下这个库,和他的实现原理。 mock主要的功能是对接口的模拟,需要在写代码的时候定义抽象很多接口,有时为了能方便go test可能会多写一些冗余代码,但这些工作会让你的单元测试更灵活。特别是逻辑比较复杂的时候,上层要调用其他层的方法进行单元测试,会让单元测试越写越麻烦,越写越复杂,这也是很多人不喜欢写单元测试的原因。使用mock模拟底层的接口,能让你只关注上层需要测试的逻辑,而不用为了测试一个功能,写一堆调用的底层的相关的测试逻辑。
lpxxn
2020/07/22
8680
go test 测试用例那些事(二) mock
golang单元测试之mock
前面介绍了golang的一般单元测试,以及如何使用vscode进行高效的go单元测试开发。同时也说过一般单元测试重点在于cpu和内存类型的测试,而对io类型的测试是比较敏感的。那么针对这类测试就没法做单元测试了吗?有的,肯定是有的,这就是mock技术。
黑光技术
2019/03/06
11.5K0
golang单元测试之mock
用Mocking技术进行MySQL数据库的单元测试(Go版)
在Go中,我们可以使用GoMock工具来创建模拟对象。GoMock是由golang团队开发的一个模拟库,它可以自动根据接口生成模拟对象,非常方便。这个库最近归档了,还可以用一段时间,同类库技术和用法都差不多。
运维开发王义杰
2023/08/10
7250
用Mocking技术进行MySQL数据库的单元测试(Go版)
gomock 源码分析
在分析完gostub的源码实现后https://mp.weixin.qq.com/s/I6urCBHbcfZCNaWw1iZmnA,我们分析下gomock的源码实现。两者原理相似,但是也有不同。
golangLeetcode
2022/08/02
3700
优雅的使用Go进行单元测试
现在我们想测试Target函数,但是由于调用的A函数依赖于自己的某个函数,这里就是A调用了rpc接口拉别人接口数据,我们想mockA接口的目标是,想直接拿到A返回的数据即可,直接采用gomock方式,行不通,自己测试了一下,发现要不断的mock 别人接口所依赖的其他接口,非常麻烦,通过注入代码或者后面第三种方式替换函数即可解决。
公众号guangcity
2020/07/20
2.9K0
Golang 单元测试 - 接口层
上次我们已经搞定了逻辑层的单元测试,这次我们来康康接口层的单元测试。接口层主要负责的就是请求的处理,最常见的就是 HTTP 请求的处理。
LinkinStar
2023/02/24
4060
Go每日一库之91:gomock
testing包里 介绍了 Go 语言中单元测试的常用方法,包括子测试(subtests)、表格驱动测试(table-driven tests)、帮助函数(helpers)、网络测试和基准测试(Benchmark)等。这篇文章介绍一种新的测试方法,mock/stub 测试,当待测试的函数/对象的依赖关系很复杂,并且有些依赖不能直接创建,例如数据库连接、文件I/O等。这种场景就非常适合使用 mock/stub 测试。简单来说,就是用 mock 对象模拟依赖项的行为。
luckpunk
2023/09/30
4560
Golang 单元测试合集整理,(我最常用 gomonkey)欢迎收藏
无论写什么样的语言,单元测试都是必不可少的,它可以极大的提高我们的代码质量,减少各种低级错误和 bug
阿兵云原生
2023/09/14
2K0
Golang 单元测试合集整理,(我最常用 gomonkey)欢迎收藏
Golang 单元测试详尽指引
文末有彩蛋。 作者:yukkizhang,腾讯 CSIG 专项技术测试工程师 本篇文章站在测试的角度,旨在给行业平台乃至其他团队的开发同学,进行一定程度的单元测试指引,让其能够快速的明确单元测试的方式方法。 本文主要从单元测试出发,对Golang的单元测试框架、Stub/Mock框架进行简单的介绍和选型推荐,列举出几种针对于Mock场景的最佳实践,并以具体代码示例进行说明。 一、单元测试 1. 单元测试是什么 单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向
腾讯技术工程官方号
2020/10/26
4.6K0
一文说尽Golang单元测试实战的那些事儿
导语 | 单元测试,通常是单独测试一个方法、类或函数,让开发者确信自己的代码在按预期运行,为确保代码可以测试且测试易于维护。腾讯后台开发工程师张力结合了公司级漏洞扫描系统洞犀在DevOps上探索的经验,以Golang为例,列举了编写单元测试需要的工具和方法,然后针对写单测遇到的各种依赖问题,详细介绍了通过Mock的方式解决各种常用依赖,方便读者在写go语言UT的时候,遇到依赖问题,能够快速找到解决方案。最后再和大家探讨一下关于单元测试上的一些思考。 一、前言 单元测试,通常是单独测试一个方法、类或函数
腾讯云开发者
2021/07/28
1.4K0
相关推荐
【初识Go】| Day12 单元测试
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验