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

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

mock对象就是为了解决上面的问题而诞生的,mock(模拟)对象能够模拟实际依赖对象的功能,同时又不需要非常复杂的准备工作,你需要做的,仅仅就是定义对象接口,然后实现它,再交给测试对象去使用。

go-mock是专门为go语言开发的mock库,该库使用方式简单,支持自动生成代码,可以说是不可多得的好工具。下面我就简单地展示一下go-mock是如何工作的:

首先你需要做的是将依赖下载到本地:

go get github.com/golang/mock/gomock
go get github.com/golang/mock/mockgen

第一个是代码依赖,第二个是命令行工具(特别好用)。

下面用一个非常简单的例子来说明gomock是如何工作的:

我在$GOPATH/src目录下新建一个项目:hellomock,在$GOPATH/src/hellomock目录下新建hellomock.go,并定义一个接口Talker:

package hellomock

type Talker interface {
  SayHello(word string)(response string)
}

然后我们需要一个实现了Talker功能的结构体,假设我们有这样的场景,我们现在有一个迎宾的岗位,需要一个能够迎宾的迎宾员,当然这个迎宾员可以是一个人,或者是一只鹦鹉。那么我们需要做的是,定义一个Persion结构体(或者是鹦鹉或者是别的什么东西),并实现Talker接口:

person.go

package hellomock

import "fmt"

type Person struct{
 name string
}

func NewPerson(name string)*Person{
 return &Person{
   name:name,
 }
}

func (p *Person)SayHello(name string)(word string) {
 return fmt.Sprintf("Hello %s, welcome come to our company, my name is %s",name,p.name)
}

现在我们的Person已经实现了Talker接口,现在我们让他发挥作用吧!

现在假设,我们有一个公司,公司有一个迎宾员,也就是我们的前台妹子,这个妹子实现了Talker接口.她能够自动向来的客人SayHello:

company.go

package hellomock

type Company struct {
 Usher Talker
}

func NewCompany(t Talker) *Company{
 return &Company{
  Usher:t,
 }
}

func ( c *Company) Meeting(gusetName string)string{
 return c.Usher.SayHello(gusetName)
}

我们的场景已经设计好了,那么我们传统的话,会如何实现单元测试呢?

company_test.go

package hellomock

import "testing"

func TestCompany_Meeting(t *testing.T) {
  person := NewPerson("王尼美")
  company := NewCompany(person)
  t.Log(company.Meeting("王尼玛"))
}

测试之:

/usr/local/go/bin/go test -v hellomock -run ^TestCompany_Meeting$

  company_test.go:8: Hello 王尼玛, welcome come to our company, my name is 王尼美

ok   hellomock  0.013s

现在我们构造一个王尼美还是很简单的,但是我们现在要用mock对象进行模拟,这时mockgen就登场了:

➜  hellomock> mkdir mock                                       
➜  hellomock> mockgen -source=hellomock.go > mock/mock_Talker.go

这个时候,将会生成mock/mock_Talker.go文件:

需要注意的是,自动生成的文件同源文件在不同的包下,需要新建一个目录存放

我们并不需要在意生成文件的内容,我们只需要知道如何去使用即可

mock_Talker.go

// Automatically generated by MockGen. DO NOT EDIT!
// Source: hellomock.go

package mock_hellomock

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

// MockTalker is a mock of Talker interface
type MockTalker struct {
  ctrl   *gomock.Controller
  recorder *MockTalkerMockRecorder
}

// MockTalkerMockRecorder is the mock recorder for MockTalker
type MockTalkerMockRecorder struct {
  mock *MockTalker
}

// NewMockTalker creates a new mock instance
func NewMockTalker(ctrl *gomock.Controller) *MockTalker {
  mock := &MockTalker{ctrl: ctrl}
  mock.recorder = &MockTalkerMockRecorder{mock}
  return mock
}

// EXPECT returns an object that allows the caller to indicate expected use
func (_m *MockTalker) EXPECT() *MockTalkerMockRecorder {
  return _m.recorder
}

// SayHello mocks base method
func (_m *MockTalker) SayHello(name string) string {
  ret := _m.ctrl.Call(_m, "SayHello", name)
  ret0, _ := ret[0].(string)
  return ret0
}

// SayHello indicates an expected call of SayHello
func (_mr *MockTalkerMockRecorder) SayHello(arg0 interface{}) *gomock.Call {
  return _mr.mock.ctrl.RecordCall(_mr.mock, "SayHello", arg0)
}

接下来看看如何去使用这个mock对象,新建一个单元测试:

func TestCompany_Meeting2(t *testing.T) {
  ctl := gomock.NewController(t)
  mock_talker := mock_hellomock.NewMockTalker(ctl)
  mock_talker.EXPECT().SayHello(gomock.Eq("王尼玛")).Return("这是自定义的返回值,可以是任意类型。")

  company := NewCompany(mock_talker)
  t.Log(company.Meeting("王尼玛"))
  //t.Log(company.Meeting("张全蛋"))
}

测试之:

/usr/local/go/bin/go test -v hellomock -run ^TestCompany_Meeting2$
  company_test.go:21: 这是自定义的返回值,可以是任意类型。
ok   hellomock  0.015s

可以看到,返回的是我们在mock对象上定义的返回值。

需要说明的一点是,mock对象的SayHello可以接受的参数有gomock.Eq(x interface{})和gomock.Any(),前一个要求测试用例入餐严格符合x,第二个允许传入任意参数。比如我们在注释掉的测试中传入了"张全蛋",结果报错,测试失败:

/usr/local/go/bin/go test -v hellomock -run ^TestCompany_Meeting2$
  controller.go:113: no matching expected call: *mock_hellomock.MockTalker.SayHello([张全蛋])
exit status 1
FAIL  hellomock  0.007s

本文作为抛砖引玉,gomock还有很多高级用法,希望大家能够自行探索。

参考文章:

https://github.com/golang/mock/blob/master/README.md

https://github.com/grpc/grpc-go/blob/master/Documentation/gomock-example.md

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

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

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

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

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

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

    目录 前言 函数 方法 参考 前言 在 Golang 语言中,写单元测试的时候,不可避免的会涉及到对其他函数及方法的 Mock,即在假设其他函数及方法响应预期结果的同时,校验被测函数的响应是否符合预期. 其中,在 Mock 其他函数及方法的时候,我们常用到的一个测试类库是「gomonkey」.特别地,对于方法和函数的 Mock,略有差异,在这里我们就分别给出函数和方法 Mock 示例,方便大家参考. 函数 在 Golang 语言中,函数是没有接受者的方法,其形式为 func function_n

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

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

  • Go单元测试对GORM进行Mock测试

    目录 前言 项目准备 初始化测试工作 对Create进行Mock测试 Get 操作的Mock测试 Update 操作的Mock测试 总结 前言 在 Go 单元测试这个系列的第二部分 数据库的Mock测试 中我们介绍了用 go-sqlmock 给数据库的 CRUD 操作做Mock 测试的方法,不过里面只是讲解了一下怎么对原生的database/sql执行的 SQL 进行 Mock 测试. 真实的开发场景下我们的项目一般都会使用 ORM ,而不是原生的database/sql来完成数据库操作.在很多

  • 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文件,文件中代码如下

  • Go单元测试对数据库CRUD进行Mock测试

    目录 go-sqlmock 安装 使用示例 miniredis 安装 使用示例 总结 最近在实践中也总结了一些如何用表格驱动的方式使用 gock Mock测试外部接口调用.以及怎么对GORM做mock测试,这些等这篇学完基础后,后面再单独写文章给大家介绍. 这是Go语言单元测试系列教程的第3篇,介绍了如何使用go-sqlmock和miniredis工具进行MySQL和Redis的mock测试. 在上一篇<Go单元测试--模拟服务请求和接口返回>中,我们介绍了如何使用httptest和gock工

  • 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

随机推荐