文章目录

  • 1.问题起源
  • 2.解决方式
    • 1.Goland配置test禁止内联优化
  • 2.修改指针/结构体调用

1.问题起源

今天想对之前匆匆写下的代码完善单元测试,使用了gomonkey对代码中的http调用和rpc调用进行打桩,核心的待测代码和测试代码如下:

  • utils.go
package utilstype MaterialUtil interface {QueryTaskByTaskId(ctx context.Context, centerTaskId int64) (resp *MaterialTask, err error)
}
type MaterialUtilImpl struct {}func (m MaterialUtilImpl) QueryTaskByTaskId(ctx context.Context, centerTaskId int64) (resp *MaterialTask, err error) {// 这里有一堆http和rpc调用resp, err = getMaterialsByTaskId(centerTaskId)// 忽略...return resp, err
}
  • material_test.go
package material_testimport ("context""github.com/agiledragon/gomonkey". "github.com/smartystreets/goconvey/convey""reflect""testing""utils"
)func TestMaterialCenterTask(t *testing.T) {Convey("test material task center", t, func() {// 使用gomonkey mock掉MaterialUtilImpl的QueryTaskByTaskId方法gomonkey.ApplyMethod(reflect.TypeOf(&utils.MaterialUtilImpl{}), "QueryTaskByTaskId", func(_ *utils.MaterialUtilImpl, ctx context.Context, centerTaskId int64) (resp *utils.MaterialTask, err error) {return &utils.MaterialTask{ErrCode: 0,Msg:     "mock调用成功",}, nil})// 测试materialUtil := &utils.MaterialUtilImpl{}resp, err := materialUtil.QueryTaskByTaskId(context.TODO(), 12345)So(err, ShouldBeNil)So(resp.Msg, ShouldEqual, "mock调用成功")})
}

单测不通过,报错,说明gomonkey ApplyMethod不生效,没有正常mock MaterialUtilImpl的QueryTaskByTaskId方法。

Failures:Expected: 'mock调用成功'Actual:   'null'(Should be equal)

2.解决方式

1.Goland配置test禁止内联优化

gomonkey对于mock方法失败的官方解决方式是启动go test时新增启动参数-gcflags=all=-l避免内联优化。
gomonkey的实现原理是获取目标函数的入口地址并将跳转至替换函数的机器码填充在目标函数的地址处,这样调用目标函数时,从目标函数的入口地址进入会直接执行go monkey填充的跳转指令,跳转到替换函数处继续执行。因此,需要在调用目标函数时,跳转至目标函数的入口地址,才可以执行go monkey填充的跳转指令。而golang编译器在编译时会进行内联优化,即把简短的函数在调用它的地方展开,从而消除调用目标函数的开销。但因为内联消除了调用目标函数时的跳转操作,使得go monkey填充在目标函数入口处的指令无法执行,因而也无法实现函数体的运行时替换,使go monkey失效。

使用goland的话在Run/Edit Configuraitons中也可以新增这个启动参数,在执行测试时附加上:

2.修改指针/结构体调用

做完上一步后发现还是不行,再次看文档示例发现一个不一样的地方,就是示例中默认认为大家都是通过结构体指针调用方法,而不是通过结构体调用方法的,但是在我们的代码实现中是会出现通过结构体调用方法的,这就是问题所在:

使用ApplyMethod时,reflect.TypeOf(caller)的caller入参和func(_ caller)的caller入参必须和原方法一致,原方法采用的是结构体调用,那么caller就必须为结构体,反之就都得为指针。

所以解决方式有两种,一种是修改原接口为指针调用,那么上述test程序就可以正确mock了,第二种是在ApplyMethod时采用结构体作为入参:

  • material_test.go
package material_testimport ("context""github.com/agiledragon/gomonkey". "github.com/smartystreets/goconvey/convey""reflect""testing""utils"
)func TestMaterialCenterTask(t *testing.T) {Convey("test material task center", t, func() {// 以MaterialUtilImpl结构体作为入参gomonkey.ApplyMethod(reflect.TypeOf(utils.MaterialUtilImpl{}), "QueryTaskByTaskId", func(_ utils.MaterialUtilImpl, ctx context.Context, centerTaskId int64) (resp *utils.MaterialTask, err error) {return &utils.MaterialTask{ErrCode: 0,Msg:     "mock调用成功",}, nil})// 测试materialUtil := &utils.MaterialUtilImpl{}resp, err := materialUtil.QueryTaskByTaskId(context.TODO(), 12345)So(err, ShouldBeNil)So(resp.Msg, ShouldEqual, "mock调用成功")})
}

Golang:gomonkey ApplyMethod 失效解决方式相关推荐

  1. 关于 Linux 的配置文件 /etc/profile 路径出错后相关的命令失效解决方式(如:ls,vi不能用)...

    关于 Linux 的配置文件 /etc/profile 路径出错后相关的命令失效解决方式(如:ls,vi不能用) 今天学习LINUX 下配置jdk 和安装tomcat 通过VI编辑/etc/profi ...

  2. 中控考勤机使用 zkemkeeper SDK订阅考勤数据事件失效解决方式

    问题 前同事编写的对中控考勤机数据集成项目当中,打卡数据不能实时进行上传到平台当中,一直靠定时全量上传来同步数据. 阅读代码后,发现代码中有实时上传数据的逻辑,但是运行一段时间后,中控zkemkeep ...

  3. jquery动态添加列表后样式失效解决方式

    最近在做一个通讯录,使用jquery mobile方式来做,在动态添加列表后,listview原有的样式失效,折腾一会,找出解决办法.代码如下. <%@ page language=" ...

  4. Mac系统 - zsh所有命令失效解决方式

    zsh: command not found: 错误一: 在~下新建了一个.bashe_profile 配置了一个环境变量 执行了source ~/.bash_profile 报错信息: Users/ ...

  5. vscode全局搜失效解决方式

  6. list remove 失效的解决方式

    list remove 失效的解决方式 不能使用list直接删除对象,要使用Iterator 效果图

  7. MySQL 索引失效的几种类型以及解决方式

    点击下方"Java编程鸭"关注并标星 更多精彩 第一时间直达 索引失效的情况有哪些? 索引列不独立 使用了左模糊 使用 or 查询部分字段没有使用索引 字符串条件没有使用 '' 不 ...

  8. ORA-00054: 资源正忙, 但指定以 NOWAIT 方式获取资源, 或者超时失效---解决方法

    问题: 解决方式: 1.-- 查询锁表session_id select session_id from v$locked_object; 2:-- 查询锁表session_id 的详细数据 USER ...

  9. Android爬坑之旅:软键盘挡住输入框问题的终极解决方式

    本文由BarryZhang原创,同一时候首发于diycode.cc.barryzhang.com .github.com/barryhappy.非商业转载请注明作者和原文链接. 前言 开发做得久了.总 ...

最新文章

  1. 人脑是怎么防止梯度消失和梯度爆炸的?
  2. 如何选择你最合适的linux系统
  3. 使用Gradle将JAR工件发布到Artifactory
  4. bestcoder #66
  5. 08-08 性能测试--分布式
  6. 西瓜书+实战+吴恩达机器学习(二十)随机算法(拉斯维加斯方法、蒙特卡罗方法)
  7. CSC 命令编译cs文件
  8. 【软件工程】山东大学软件工程复习提纲
  9. 如何电脑上怎样查看微信聊天记录
  10. mysql cleaned up_关于mysqld自动停止的问题
  11. 传说中开场晕过去一千多人的迈克杰克逊演唱会,有生之年请看下这个视频
  12. Shema与数据类型优化
  13. 目标检测~FastPillars实时3D
  14. A. Extreme Extension
  15. 阿里云轻量应用服务器Ubuntu20.04上手体验与基本配置(图形界面,ssh,代理等)
  16. C/C++黑魔法-防御性编程
  17. 关于孟岩一篇文章的讨论
  18. 机器学习与人工智能顶会论文列表汇总
  19. 阿里:不清除35岁以上的P8员工!
  20. PHP用户注册页面测试用例,用户注册界面测试用例

热门文章

  1. 计算机课程合并建,信息技术与课程整合的三个阶段
  2. java异常throws怎么用,throw_JAVA的throw和throws怎么用!
  3. 喜马拉雅 FM--- [ Java 高级开发] [ Java 架构师] [iOS 架构师] 招聘中~
  4. echarts x轴换行
  5. 计算机实验小学教导主任,实验小学教导主任岗位职责(2页)-原创力文档
  6. 有些流氓软件删除不掉如何处理
  7. mybatis查询千万数据如何保证内存够用?mybatis流式查询
  8. 达人评测 i5 1340P和i5 13420H对比 酷睿i51340P和i513420H差距
  9. 新手入门:调环境,快逼到玄学了该怎么办
  10. 图书借阅管理系统c语言程序设计,图书管理系统课程设计