使用gomock实现单元测试

gomock

        • 安装
        • 接口文件
        • 生成代码
        • 使用MOCK
        • 分析mock生成的代码
        • 结尾

安装

包下载:go get github.com/golang/mock/gomock
获取文档:go doc github.com/golang/mock/gomock
代码生成包:go get github.com/golang/mock/mockgen

接口文件

//定义接口文件,如下。并实现接口
package demo

type demoInter interface {
    DemoAction() string
}

生成代码

  1. source: 指定接口文件
  2. destination: 生成的文件名
  3. package:生成文件的包名
  4. imports: 依赖的需要import的包
  5. aux_files:接口文件不止一个文件时附加文件
  6. build_flags: 传递给build工具的参数
mockgen -destination=./mock/mock_demo.go --source=./demo.go -package=mock_demo

使用MOCK

  1. 创建XXX_test.go文件
  2. 创建测试方法: TestXXX(t *testing.T) {}
  3. 方法中使用mock:
    a.创建控制器:c := gomock.NewController(t)
    b.实例化mock:rs := mock_demo.NewXXX(c)
    c.设置对应方法的返回:rs.EXPECT().DemoAction().Return(data, nil)
    d.调用对应方法
    e.判断结果

分析mock生成的代码

  1. gomock.NewController(t):返回1个控制实例
  2. NewMockXXX(c):
//生成的mock类型
type MockXXX struct {
	ctrl     *gomock.Controller
	recorder *MockXXXMockRecorder
}
//这里主要是实例化根据你之前定的接口而生成的mock类型
func NewXXXTool(ctrl *gomock.Controller) *MockXXX {
	mock := &MockXXX{ctrl: ctrl}
	mock.recorder = &MockXXXMockRecorder{mock}
	return mock
}

3.ts.EXPECT().DemoAction().Return(data, nil)

 //EXPECT() 返回允许调用方指示预期用途的对象 
 func (m *MockXXXTool) EXPECT() *MockXXXMockRecorder {
	return m.recorder
}
 //DemoAction() 这里主要是模拟基础方法,返回同你真正定义的方法
 func (m *MockXXXTool) DemoAction() (XXX, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "DemoAction")
	ret0, _ := ret[0].(XXX)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}
//初始化模拟的方法
func (mr *MockXXXMockRecorder) DemoAction() *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListById", reflect.TypeOf((*MockXXXTool)(nil).DemoAction))
}
//Return() 追加模拟的方法和模拟的参数和返回到 *Call内
func (c *Call) Return(rets ...interface{}) *Call {
	c.t.Helper()

	mt := c.methodType
	//判断函数的出参个数是否和初始化的一致
	if len(rets) != mt.NumOut() {
		c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d [%s]",
			c.receiver, c.method, len(rets), mt.NumOut(), c.origin)
	}
	//循环判断参数的类型是否和初始化的一致
	for i, ret := range rets {
		if got, want := reflect.TypeOf(ret), mt.Out(i); got == want {
			// 如果类型为空
		} else if got == nil {
			switch want.Kind() {
			case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
				// ok
			default:
				c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable [%s]",
					i, c.receiver, c.method, want, c.origin)
			}
		} else if got.AssignableTo(want) {
			// 判断该类型能否赋值给某个类型
			v := reflect.New(want).Elem()
			v.Set(reflect.ValueOf(ret))
			rets[i] = v.Interface()
		} else {
			c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v [%s]",
				i, c.receiver, c.method, got, want, c.origin)
		}
	}
	//将方法追加到模拟方法库中
	c.addAction(func([]interface{}) []interface{} {
		return rets
	})

	return c
}

结尾

所以就是模拟你要测试的方法中的所需要到的其他方法。比如数据库操作,rpc方法等。

一千个人眼中有一千个哈姆雷特,但是一定要多多研究和思考!加油!

你可能感兴趣的:(GO,单元测试)