搞单元测试,如果碰到这些情况:
1,一个函数,内部包含了很多并且很深的调用,但是如果单单测这个函数,其实实现的功能很简单。
2,一个函数,包含了其他还未实现的调用。
3,函数内部对数据的要求极为苛刻。
那么这时候就可以考虑使用mock来处理。
mock,简而言之就是可以通过注入我们所期望返回的数据,或者我们所期望传递的参数,来避免上面那些情况,其原理则是通过反射来实现。
这次就来看看golang的mock,gomock
gomock是go官方提供的mock解决方案,主要分为两部分:gomock库和mock代码生成工具mockgen。
使用举例:
package metal
type Imetal interface {
GetName() string
SetName(string) string
}
type Metal struct {
Name string
Exchange string
}
func (self Metal) GetName() string {
if self.Name==""{
return "none"
}
return self.Name
}
func (self *Metal) SetName(brand string) string {
self.Name=brand
return "done"
}
我现在有一个package,其包含了IMetal接口,这个接口下面有两个方法,现在针对这两个方法来进行mock,ps:gomock只支持interface方法的mock。
在mock之前,需要先通过mockgen来生成mock代码,我的源就是上面的IMeta接口。
简单介绍一下mockgen:
它有两种工作模式---source和reflect
source模式
mockgen -source=foo.go [other options]
根据源文件来生成,源文件是包含了一个或多个interface的文件。
reflect模式
mockgen src/package Conn,Driver
一个文件定义了多个interface而你只想对部分interface进行mock,或者interface存在嵌套,使用reflect模式
mock代码生成好之后,接下来是写测试函数。
package metal
import (
"mock_metal"
"github.com/golang/mock/gomock"
"testing"
"fmt"
)
func GetMetalName(mi Imetal) string {
mi.GetName()
return mi.GetName()
}
func SetMetalName(mi Imetal,name string) string {
return mi.SetName(name)
}
func TestMetalName(t *testing.T) {
mockCtl := gomock.NewController(t)
defer mockCtl.Finish()
mockMetal := mock_metal.NewMockImetal(mockCtl) //mock_metal就是生成的mock代码,以包的形式存在
m:=new(Metal)
mockCtl.RecordCall(m,"GetName").Times(1)
mockCtl.Call(m,"GetName")
call:=mockMetal.EXPECT().GetName().Return("apple")
mockMetal.EXPECT().GetName().Return("peer").After(call) //注入期望的返回值
mockedBrand:=GetMetalName(mockMetal)
mockMetal.EXPECT().SetName(gomock.Eq("al")).Do(func(format string) { //入参校验
fmt.Println("recv param :",format)
}).Return("setdone")
mockMetal.EXPECT().SetName(gomock.Any()).Do(func(format string) { //入参不做校验
fmt.Println("recv param :",format)
}).Return("setdone")
mockedSetName:=SetMetalName(mockMetal,"al")
fmt.Println(mockedSetName)
if "peer"!=mockedBrand{
t.Error("Get wrong name:", mockedBrand)
}
if "setdone"!=mockedSetName{
t.Error("Set wrong name:", mockedSetName)
}
}
然后执行go test即可,会发现这些被mock的函数会按照我们定义的行为来执行。
附带两个gomock的官方资料,基本上看完了就可以上手了。
https://godoc.org/github.com/golang/mock/gomock
https://github.com/golang/mock/blob/master/sample/user_test.go
第一个是gomock库的所有方法说明,第二个是官方的例子,里面有如何进行gomock的方法使用。