github.com/tencent/goom@v1.0.1/builder.go (about) 1 // Package mocker 定义了 mock 的外层用户使用 API 定义, 2 // 包括函数、方法、接口、未导出函数(或方法的)的 Mocker 的实现。 3 // 当前文件实现了 Mocker 接口各实现类的构造链创建,以便通过链式构造一个 Mocker 对象。 4 package mocker 5 6 import ( 7 "fmt" 8 "reflect" 9 "runtime" 10 "strings" 11 12 "github.com/tencent/goom/internal/iface" 13 "github.com/tencent/goom/internal/logger" 14 "github.com/tencent/goom/internal/patch" 15 ) 16 17 // Builder Mock 构建器, 负责创建一个链式构造器. 18 type Builder struct { 19 pkgName string 20 mockers map[interface{}]Mocker 21 } 22 23 // Pkg 指定包名,当前包无需指定 24 // 对于跨包目录的私有函数的 mock 通常都是因为代码设计可能有问题, 此功能会在未来版本中移除 25 // 后续仅支持同包下的未导出方法的 mock 26 // Deprecated: 对于跨包目录的私有函数的 mock 通常都是因为代码设计可能有问题 27 func (b *Builder) Pkg(name string) *Builder { 28 b.pkgName = name 29 return b 30 } 31 32 // PkgName 返回包名 33 // Deprecated: 对于跨包目录的私有函数的 mock 通常都是因为代码设计可能有问题 34 func (b *Builder) PkgName() string { 35 return b.pkgName 36 } 37 38 // Create 创建 Mock 构建器 39 // 非线程安全的,不能在多协程中并发地 mock 或 reset 同一个函数 40 func Create() *Builder { 41 // callerDeps 当前的调用栈栈层次 42 const callerDeps = 2 43 return &Builder{ 44 pkgName: currentPkg(callerDeps), 45 mockers: make(map[interface{}]Mocker, 30), 46 } 47 } 48 49 // Interface 指定接口类型的变量定义 50 // iFace 必须是指针类型, 比如 i 为 interface 类型变量, iFace 传递&i 51 func (b *Builder) Interface(iFace interface{}) *CachedInterfaceMocker { 52 mKey := reflect.TypeOf(iFace).String() 53 if mocker, ok := b.mockers[mKey]; ok && !mocker.Canceled() { 54 b.reset2CurPkg() 55 return mocker.(*CachedInterfaceMocker) 56 } 57 58 // 创建 InterfaceMocker 59 // context 和 interface 类型绑定 60 mocker := NewDefaultInterfaceMocker(b.pkgName, iFace, iface.NewContext()) 61 cachedMocker := NewCachedInterfaceMocker(mocker) 62 b.cache(mKey, cachedMocker) 63 b.reset2CurPkg() 64 return cachedMocker 65 } 66 67 // cache 添加到缓存 68 func (b *Builder) cache(mKey interface{}, cachedMocker Mocker) { 69 b.mockers[mKey] = cachedMocker 70 } 71 72 // Struct 指定结构体实例 73 // 比如需要 mock 结构体函数 (*conn).Write(b []byte),则 name="conn" 74 func (b *Builder) Struct(instance interface{}) *CachedMethodMocker { 75 mKey := reflect.ValueOf(instance).Type().String() 76 if mocker, ok := b.mockers[mKey]; ok && !mocker.Canceled() { 77 b.reset2CurPkg() 78 return mocker.(*CachedMethodMocker) 79 } 80 81 mocker := NewMethodMocker(b.pkgName, instance) 82 cachedMocker := NewCachedMethodMocker(mocker) 83 b.cache(mKey, cachedMocker) 84 b.reset2CurPkg() 85 return cachedMocker 86 } 87 88 // Func 指定函数定义 89 // funcDef 函数,比如 foo 90 // 方法的 mock, 比如 &Struct{}.method 91 func (b *Builder) Func(funcDef interface{}) *DefMocker { 92 funcPointer := reflect.ValueOf(funcDef).Pointer() 93 key := runtime.FuncForPC(funcPointer).Name() 94 // 对于包含泛型参数的函数,可以附加函数指针作为key来区分不同泛型变量类型的函数 95 if patch.IsGenericsFunc(key) { 96 key = key + fmt.Sprintf("-%x", funcPointer) 97 } 98 if mocker, ok := b.mockers[key]; ok && !mocker.Canceled() { 99 b.reset2CurPkg() 100 return mocker.(*DefMocker) 101 } 102 103 mocker := NewDefMocker(b.pkgName, funcDef) 104 b.cache(key, mocker) 105 b.reset2CurPkg() 106 return mocker 107 } 108 109 // ExportStruct 导出私有结构体 110 // 比如需要 mock 结构体函数 (*conn).Write(b []byte),则 name="conn" 111 func (b *Builder) ExportStruct(name string) *CachedUnexportedMethodMocker { 112 if mocker, ok := b.mockers[b.pkgName+"_"+name]; ok && !mocker.Canceled() { 113 b.reset2CurPkg() 114 return mocker.(*CachedUnexportedMethodMocker) 115 } 116 117 structName := name 118 if strings.Contains(name, "*") { 119 structName = fmt.Sprintf("(%s)", name) 120 } 121 122 mocker := NewUnexportedMethodMocker(b.pkgName, structName) 123 cachedMocker := NewCachedUnexportedMethodMocker(mocker) 124 b.cache(b.pkgName+"_"+name, cachedMocker) 125 b.reset2CurPkg() 126 return cachedMocker 127 } 128 129 // ExportFunc 导出私有函数 130 // 比如需要 mock 函数 foo(), 则 name="pkg_name.foo" 131 // 比如需要 mock 方法, pkg_name.(*struct_name).method_name 132 // name string foo 或者(*struct_name).method_name 133 func (b *Builder) ExportFunc(name string) *UnexportedFuncMocker { 134 if name == "" { 135 panic("func name is empty") 136 } 137 138 if mocker, ok := b.mockers[b.pkgName+"_"+name]; ok && !mocker.Canceled() { 139 b.reset2CurPkg() 140 return mocker.(*UnexportedFuncMocker) 141 } 142 143 mocker := NewUnexportedFuncMocker(b.pkgName, name) 144 b.cache(b.pkgName+"_"+name, mocker) 145 b.reset2CurPkg() 146 return mocker 147 } 148 149 // Var 变量 mock, target 类型必须传递指针类型 150 func (b *Builder) Var(v interface{}) VarMock { 151 cacheKey := fmt.Sprintf("var_%d", reflect.ValueOf(v).Pointer()) 152 if mocker, ok := b.mockers[cacheKey]; ok && !mocker.Canceled() { 153 return mocker.(VarMock) 154 } 155 156 mocker := NewVarMocker(v) 157 b.cache(cacheKey, mocker) 158 return mocker 159 } 160 161 // Reset 取消当前 builder 的所有 Mock 162 func (b *Builder) Reset() *Builder { 163 for _, mocker := range b.mockers { 164 mocker.Cancel() 165 // callerDeps 当前的调用栈栈层次 166 const callerDeps = 5 167 logger.Consolefc(logger.DebugLevel, "mockers [%s] resets.", logger.Caller(callerDeps), mocker.String()) 168 } 169 return b 170 } 171 172 // reset2CurPkg 设置回当前的包 173 func (b *Builder) reset2CurPkg() { 174 b.pkgName = currentPackage() 175 } 176 177 // currentPackage 获取当前调用的包路径 178 func currentPackage() string { 179 // callerDeps 当前的调用栈栈层次 180 const callerDeps = 4 181 return currentPkg(callerDeps) 182 } 183 184 // currentPkg 获取调用者的包路径 185 func currentPkg(skip int) string { 186 pc, _, _, _ := runtime.Caller(skip) 187 callerName := runtime.FuncForPC(pc).Name() 188 189 if i := strings.Index(callerName, ".("); i > -1 { 190 return callerName[:i] 191 } 192 193 if i := strings.LastIndex(callerName, "/"); i > -1 { 194 realIndex := strings.Index(callerName[i:len(callerName)-1], ".") 195 return callerName[:realIndex+i] 196 } 197 198 realIndex := strings.Index(callerName, ".") 199 return callerName[:realIndex] 200 } 201 202 // OpenDebug 开启 debug 模式 203 // 也可以通过添加环境变量开启 debug: GOOM_DEBUG=true 204 // 1.可以查看 apply 和 reset 的状态日志 205 // 2.查看 mock 调用日志 206 func OpenDebug() { 207 logger.OpenDebug() 208 } 209 210 // CloseDebug 关闭 debug 模式 211 func CloseDebug() { 212 logger.CloseDebug() 213 } 214 215 // OpenTrace 打开日志跟踪 216 func OpenTrace() { 217 logger.OpenTrace() 218 } 219 220 // CloseTrace 关闭日志跟踪 221 func CloseTrace() { 222 logger.CloseTrace() 223 }