Go语言单测方案之辩

提出问题

很多公司会要求代码的单测覆盖率,在编写单测时难免会遇到一些问题,想象一个场景

Go 复制代码
func A(){
    aInt := 1
    aStr := "a"
    idx, err := B(aInt, aStr)
    if err!= nil {
        return C(idx)
    }
    return err
}

func B(){
    X()
    Y()
}

当我对func A进行测试时,我并不关注func B内部的实现细节,涉及远程调用,数据库连接等原因,我也并不希望func B真的调用其他服务。

因此我希望使用一个"假函数", 规定好他的输入与输出。那么如何实现这个功能呢?一般情况下会有两种思路:

  • 以monkey为主流的直接对函数进行Mock
  • 依赖倒置

实际上也会有其他方法,但是并非主流,此处不赘述。

Mock与依赖倒置的比较

gomonkey

monkey本身在开源协议上有些局限,作者虽然公开了代码,但并不允许任何商用:

Copyright Bouke van der Bijl. I do not give anyone permissions to use this tool for any purpose. Don't use it. I'm not interested in changing this license. Please don't ask.

所以我们这里主要聚焦在gomonkey

gomonkey的实现方式很简单,

Golang 复制代码
gomonkey.ApplyMethod(type, func_name, mock_func)

在使用上极度简单的同时,也隐藏着一些隐患,gomonkey实现的原理是在代码运行时直接替代待调用函数的指针地址。这本质上是不安全的,所以出现了以下问题:

  • 苹果觉得不安全,所以在M1芯片上不允许使用gomonkey,导致大多数互联网员工不能随意使用gomonkey
  • 直接修改指针地址本身并不并发安全,因此一旦并发mock,可能就会出现未知的问题。
  • 当前gomonkey的底层实现依赖go语言底层ABI(application binary interface),Go只保证在API上的向下兼容,但并不保证ABI(比如曾经基于栈来实现的abi为了更高的性能改为通过寄存器来实现)

综上,gomonkey的优点是方便快捷,但是在兼容性稳定性并发安全上具有不可忽视的问题。

依赖倒置

通过依赖倒置来让我们可以简单的实现构造一个"假函数"的目的。依赖倒置本质上就是将A->B的关系变为 A(interface{func B}),让我们可以通过依赖注入的方式让函数A屏蔽掉函数B的影响,举个例子

Golang 复制代码
// 更改前
func B(){
    anyPackage.C("arg")
}
func C(arg string){
    fmt.Println(arg)
}
// 更改为
type ServiceCIface interface {
    C(arg string)
}
type ServiceCImpl struct {}
func (s *ServiceCImpl) C(arg string){fmt.Println(arg)}
func B(srv ServiceCIface){
    srv.C(arg)
}
func A(){
    srv := newServiceCImpl()
    B(srv)
}

显然地,代码量多了几个量级,同时依赖注入本身对业务代码也有很强的侵入性,或许我的函数依赖了很多个函数,则会有如下问题:

  • 如果被依赖函数本身只是某个公共函数,那本不需要写对应的接口,这些代码对业务实现本身都是冗余的
  • 假设函数A依赖BCDE,那么在输入上可能增加了四个参数,可读性也会差很多。
  • 如果BCDE不属于同一个包,B包下也会用其他的函数 <math xmlns="http://www.w3.org/1998/Math/MathML"> B 1 B_1 </math>B1 <math xmlns="http://www.w3.org/1998/Math/MathML"> B 2 B_2 </math>B2, 为了省事可能会倾向写包括三个函数B, <math xmlns="http://www.w3.org/1998/Math/MathML"> B 1 B_1 </math>B1 <math xmlns="http://www.w3.org/1998/Math/MathML"> B 2 B_2 </math>B2 的大接口,但是单测mock时其实没必要实现函数 <math xmlns="http://www.w3.org/1998/Math/MathML"> B 1 B_1 </math>B1 <math xmlns="http://www.w3.org/1998/Math/MathML"> B 2 B_2 </math>B2。

综上:依赖注入安全稳定平台兼容性好,但是会添加很多冗余的代码,降低可读性。

相关推荐
墨染青竹梦悠然9 小时前
基于Django+vue的图书借阅管理系统
前端·vue.js·后端·python·django·毕业设计·毕设
怪兽毕设9 小时前
基于Django的洗衣服务平台设计与实现
后端·python·django·洗衣服务平台
程序员泠零澪回家种桔子10 小时前
微服务日志治理:ELK 栈实战指南
后端·elk·微服务·云原生·架构
qq_124987075310 小时前
基于html的书城阅读器系统的设计与实现(源码+论文+部署+安装)
前端·vue.js·spring boot·后端·mysql·信息可视化·html
CodeToGym10 小时前
【全栈进阶】Spring Boot 整合 WebSocket 实战:从实时告警到金融行情推送
java·后端·spring
Leinwin10 小时前
Moltbot 部署至 Azure Web App 完整指南:从本地到云端的安全高效跃迁
后端·python·flask
毕设源码-邱学长10 小时前
【开题答辩全过程】以 基于Springboot个人健康运动系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
愿你天黑有灯下雨有伞10 小时前
Spring Boot + FastExcel:打造完美的导入校验功能
java·spring boot·后端
云霄IT10 小时前
go语言post请求遭遇403反爬解决tls/ja3指纹或Cloudflare防护
开发语言·后端·golang
Dragon Wu10 小时前
OpenAPI 3.0(Swagger3/Knife4j)完整简洁注解清单
spring boot·后端·springboot