• go 单元测试整理


    go 单元测试写法
    参考 https://studygolang.com/articles/22982

    testing
    go语言package提供的自动化测试框架,
    testing支持普通用例测试、压力测试、并行测试,基本能满足单测需求;

    缺失:

    testing不支持mock数据,想要mock数据必须自己实现mock逻辑,这会增加许多不必要的工作量。

    mock的重要性
    理想情况下,一个好的函数结构必须有入参、出参,完成的是一项单一、独立的工作,比如:

    func add(a int, b int) int {
    return a + b
    }
    这样的函数写单测很简单,且轻易就能达到100%覆盖率。

    但实际工作中,这样纯粹的函数占比可能不到10%,单个函数往往不是独立的,它需要依赖于其他模块、三方库、数据库等等。

    以上传图片为例,上传一张图片样本函数:

    func uploadImage(content []byte, UID string, fdfs *tsFDFS) error {
    // 1. 上传图片到fdfs
    url := fdfs.Upload(content)
    // 2. 生成缩略图
    resize.Resize(…)
    jpeg.Encode(…)
    // 3. 图片入库
    sample := &database.yangbenModel{ … }
    database.CreateSample(…)
    // 4. 图片绑定任务
    binding := &database.ExamplennotationTask{ … }
    database.CreateBinding(…)
    return nil
    }
    如果在单测中想要正常走完这个函数且不出错,我们需要有一个真实的fdfs环境可以上传数据成功,一个真实的数据库,且根据业务逻辑,我们想要插入一个sample前,必须有对应的enterprise、pipeline、annotation-task等等;

    于是,在此前,项目中使用testing的单测实现思路是这样的:

    func TestUploadImage(t *testing.T) {
    // 1. 创建真实的fdfs环境
    fdfs := &FDFS{ … }
    // 2. 连接真实的数据库
    db := database.NewGormConn(…)
    // 3. 插入一条enters数据
    enters := &database.EnterpriseModel{…}
    database.CreateEnters(…)
    // 4. 插入一条业务数据

    // 5. 插入另外一条业务数据

    // 6. 开始执行单测
    uploadImage(…)

    }
    可以看到,单测之前进行了大量耗时的、冗余的、无意义的工作

    单测执行耗时长,正常一条单测时间一般在100ms内,在模拟了真实场景后,单测时间大幅增加,甚至项目中有部分单测超过1分钟;
    单测编码效率低,需要了解了业务场景后,才能编写完一条单测case;
    过于依赖真实环境,稳定性差,维护成本高,对于一些无法模拟真实环境的流程,只能放弃覆盖测试。
    mock框架
    gomonkey 单元测试
    安装

    go get -u -v https://github.com/agiledragon/gomonkey

    github地址为:https://github.com/agiledragon/gomonkey
    参考博客: https://studygolang.com/articles/15034
    目前自己尝试过mock方法 把代码使用的粘贴供gopher参考下
    这个地方是rpc调用其他服务,
    测试中用打桩的方式,将这个
    函数的结果先执行了,当执行
    这个函数时,采用这个函数
    打桩时mock的数据就可以了

    在这里插入图片描述
    在这里插入图片描述
    测试文件中如何写mock第三方数据呢?
    ApplyMethod 第三个参数
    func() 中的第一个参数为这个
    方法属于那个类型,剩余的参数
    书写方式为 func(_ 绑定的那个类型,
    ,_ type)
    例如
    func(_ 绑定的那个类型,
    ,_ string, _ int)(返回结果)

    在这里插入图片描述

    gostub
    对全局变量、函数或过程打桩,对代码中的外部依赖进行劫持并替换成mock的内容

    为一个全局变量打桩
    stubs := gostub.Stub(&num, 10)
    defer stubs.Reset()
    为一个函数打桩
    stubs := gostub.StubFunc(&Func, func(args string) error {
    return nil
    })
    defer stubs.Reset()
    为一个过程打桩
    stubs := gostub.StubFunc(&Destroy)
    defer stubs.Reset()
    场景组合
    以上文测试上传一张图片样本为例

    func TestUploadImage(t *testing.T) {
    // 1. stub fdfs
    stubs := gostub.StubFunc(&Upload, func() string {
    return “”
    })
    defer stubs.Reset()

    // 2. stub 生成缩略图
    stubs = gostub.StubFunc(&resize.Resize, func() error {
    	return nil
    })
    // 3. stub sample create
    stubs = gostub.StubFunc(&database.CreateSample, func() error {
    	return nil
    })
    ...
    // 4. stub binding create
    ...
    // 5. 开始执行单测
    uploadImage(...)
    ...
    

    }
    缺点
    对代码有一定的侵入性 (需要将函数改为全局变量语法形式、对三方库需要做一层包装)
    无法对方法(成员函数)进行打桩
    monkey
    monkey是go的一个猴子补丁(monkeypatching)框架,在运行时通过汇编语句重写可执行文件,将待打桩函数或方法的实现跳转到桩实现,原理和热补丁类似。

    通过monkey可以解决stub无法为方法打桩的问题,但monkey不是线程安全的,不能用于并发测试。

    为一个函数打桩
    guard := monkey.Patch(Func, func(args string) error {
    return nil
    })

    defer guard.Unpatch()
    为一个方法打桩
    guard := monkey.PatchInstanceMethod(reflect.TypeOf(client), “Test”, func(args string) error {
    return nil
    })

    defer guard.Unpatch()
    缺点
    线程不安全
    在macOS 10.15及以上版本无法运行,由于其原理是修改可执行文件,从Catalina版本起,系统对文件读写权限做了更严格的管理,monkey修改的文件无写权限,运行panic,参考https://github.com/agiledragon/gomonkey/issues/10

  • 相关阅读:
    【团队作业冲刺'十日谈'】第七天——端侧部署6、记录保存
    团队冲刺第六天端侧部署5,模型下载功能2
    团队冲刺第五天端侧部署4,模型下载
    冲刺第四天 端侧部署3,登陆页面2
    冲刺第三天 端侧部署2,登录功能
    冲刺第二天模型训练2+端侧部署
    每日总结4.27
    每日总结4.26
    Jenkins基于https的k8s配置
    快速搭建私有gitlab
  • 原文地址:https://www.cnblogs.com/liuqun/p/14076639.html
Copyright © 2020-2023  润新知