agiledragon / gomonkey

gomonkey is a library to make monkey patching in unit tests easy
MIT License
1.93k stars 178 forks source link

请问如何实现一个通用的Mock函数? #109

Open soluty opened 1 year ago

soluty commented 1 year ago

在做单元测试的情况下, 会有一种需求是, 确定调用了某个函数,或者某个接口, 其它的mock框架基本都提供这种能力, gomonkey也可以, 但是需要一个一个的模拟, 有没有可能通过1.18的泛型, 写一个类似这样的Func结构 type Func[T any] struct { callCount int } 然后有这样的方法 MockFunc[T any](ps Patches, f T) Func[T]

那么对于只需要知道方法有没有调用的情况下, 就可以直接用这个通用的Mock结构, 甚至更进一步, 在Func中还可以存储调用的入参, 在单测中就可以精确的知道入参了.

我尝试着实现了一下我的想法, 但是由于我对反射理解有些欠缺, 所以没能完成这个, 你觉得这个想法能实现吗? 如果可以,能否做为feature加到gomonkey中?

soluty commented 1 year ago

好像不算特别麻烦, 下面的代码可以对函数的调用进行通用mock, 我再想想Api的设计, 看看可不可以基于gomonkey有一个方便的对函数和接口的被调用的包

type Func struct { call bool params []interface{} }

func MockFunc[F any](ps gomonkey.Patches, f F) Func { ret := &Func{} typ := reflect.TypeOf(f) if typ.Kind() != reflect.Func { panic("f must be a function") } rFn := reflect.MakeFunc(typ, func(args []reflect.Value) (results []reflect.Value) { for i := 0; i < typ.NumIn(); i++ { ret.params = append(ret.params, args[i].Interface()) } return }) ps.ApplyFunc(f, rFn.Interface()) return ret }

soluty commented 1 year ago

好像这个泛型没啥用..不如直接用interface

好像不算特别麻烦, 下面的代码可以对函数的调用进行通用mock, 我再想想Api的设计, 看看可不可以基于gomonkey有一个方便的对函数和接口的被调用的包

type Func struct { call bool params []interface{} }

func MockFunc[F any](ps gomonkey.Patches, f F) Func { ret := &Func{} typ := reflect.TypeOf(f) if typ.Kind() != reflect.Func { panic("f must be a function") } rFn := reflect.MakeFunc(typ, func(args []reflect.Value) (results []reflect.Value) { for i := 0; i < typ.NumIn(); i++ { ret.params = append(ret.params, args[i].Interface()) } return }) ps.ApplyFunc(f, rFn.Interface()) return ret }

好像这个泛型没啥用, 直接用interface{}就可以了

xhd2015 commented 3 months ago

xgo刚好支持通用的拦截器: https://github.com/xhd2015/xgo?tab=readme-ov-file#usage