gostub源码分析

Posted golang算法架构leetcode技术php

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gostub源码分析相关的知识,希望对你有一定的参考价值。

GoMock非常优秀,但是对于普通的函数打桩来说也有一些缺点:


必须引入额外的抽象(interface)

打桩过程比较重

既有代码必须适配新增的抽象


那么有没有轻量级的打桩方法呢?答案是gostub

全局变量可通过GoStub框架打桩

过程可通过GoStub框架打桩

函数可通过GoStub框架打桩


1,gostub 的用法

变量打桩

var counter = 100stubs := gostub.Stub(&counter, 200)


函数打桩

func Exec(cmd string, args ...string) (string, error) {}

这种函数没法直接用gostub的,Golang支持闭包,这使得函数可以作为另一个函数的参数或返回值,而且可以赋值给一个变量。

需要改成

var Exec = func(cmd string, args ...string) (string, error) {}

现在我们可以对Exec函数打桩了,代码如下所示:

stubs := Stub(&Exec, func(cmd string, args ...string) (string, error) {return "xxx-vethName100-yyy", nil})defer stubs.Reset()

其实GoStub框架专门提供了StubFunc函数用于函数打桩

stubs := StubFunc(&Exec,"xxx-vethName100-yyy", nil)defer stubs.Reset()

对于Golang的库函数或第三方的库函数,可以定义库函数的变量作为适配层:

package adaptervarStat = os.Statvar


2,gostub的源码分析

gostub 源码很简单,只有gostub.go一个源文件

核心原理就是利用反射进行值的替换


以函数打桩的接口函数为例:

func StubFunc(funcVarToStub interface{}, stubVal ...interface{}) *Stubs { return New().StubFunc(funcVarToStub, stubVal...)}

其实底层和变量替换的实现是差不多的。

核心的数据如下,存储了最初的原始变量,便于在stub结束后数据的恢复,用stubs 变量存储了被替换的变量和替换后变量的映射。

type Stubs struct { // stubs is a map from the variable pointer (being stubbed) to the original value. stubs map[reflect.Value]reflect.Value origEnv map[string]envVal}

核心的stub函数如下:

1,先通过反射获取被替换变量的值,和即将替代的值

2,把原始的值用刚刚讲的map存起来

3,修改被替换变量的值

4,用到了反射的核心函数有

reflect.ValueOf //获取interface的值

v.Elem().Interface()//获取interface 包含的值的接口

v.Elem().Set(stub) //修改interface 包含的值

func (s *Stubs) Stub(varToStub interface{}, stubVal interface{}) *Stubs { v := reflect.ValueOf(varToStub) stub := reflect.ValueOf(stubVal)
if _, ok := s.stubs[v]; !ok { // Store the original value if this is the first time varPtr is being stubbed. s.stubs[v] = reflect.ValueOf(v.Elem().Interface()) }
// *varToStub = stubVal v.Elem().Set(stub) return s}

恢复最初定义变量或者函数

func (s *Stubs) Reset() { for v, originalVal := range s.stubs { v.Elem().Set(originalVal) }}

对于函数替换过程稍微有点复杂,需要对函数的内容进行重新构造,具体代码如下

func FuncReturning(funcType reflect.Type, results ...interface{}) reflect.Value { return reflect.MakeFunc(funcType, func(_ []reflect.Value) []reflect.Value { return resultValues })}

构造函数的过程用到了反射的MakeFunc方法

reflect.MakeFunc //用给定函数来构造出funcType类型的函数,底层和c函数的转换类似,就是函数指针的转换。


以上是关于gostub源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Go语言开发(二十)GoStub测试框架

Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段

Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段

Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段

《Docker 源码分析》全球首发啦!

mysql jdbc源码分析片段 和 Tomcat's JDBC Pool