美文网首页
go 单元测试(二)mock

go 单元测试(二)mock

作者: wayyyy | 来源:发表于2022-08-20 03:07 被阅读0次

    当待测试的函数/对象的依赖关系很复杂,并且有些依赖不能直接创建,例如数据库连接、文件I/O等。这种场景就非常适合使用 mock/stub 测试。简单来说,就是用 mock 对象模拟依赖项的行为。

    gomock

    gomock 是官方提供的mock框架,同时还提供了mockgen工具用来辅助申城测试代码:
    首先使用如下命令安装:

    go get -u github.com/golang/mock/gomock
    go get -u github.com/golang/mock/mockgen
    

    然后再使用如下命令生成,

    # mockgen -source=[源文件] -destination=[生成后的mock文件] -package=[包名]
    

    会将源文件的接口生成mock对象。


    gomonkey 库

    试用了官方的,发现要先生成文件等很麻烦,搜索得到可以使用 gomonkey 这个库来mock对象。

    gomonkey 提供了如下 mock 方法:

    • ApplyGlobalVar(target, double interface{}):使用 reflect 包,将 target 的值修改为 double
    • ApplyFuncVar(target, double interface{}):检查 target 是否为指针类型,与 double 函数声明是否相同,最后调用 ApplyGlobalVar
    • ApplyFunc(target, double interface{}):修改 target 的机器指令,跳转到 double 执行
    • ApplyMethod(target reflect.Type, methodName string, double interface{}):修改 method 的机器指令,跳转到 double 执行
    • ApplyFuncSeq(target interface{}, outputs []OutputCell):修改 target 的机器指令,跳转到 gomonkey 生成的一个函数执行,每次调用会顺序从 outputs 取出一个值返回
    • ApplyMethodSeq(target reflect.Type, methodName string, outputs []OutputCell):修改 target 的机器指令,跳转到 gomonkey 生成的一个方法执行,每次调用会顺序从 outputs 取出一个值返回
    • ApplyFuncVarSeq(target interface{}, outputs []OutputCell):gomonkey 生成一个函数顺序返回 outputs 中的值,调用 ApplyGlobalVar

    但是有2点需要注意:

    • monkey不支持内联函数,在测试的时候需要通过命令行参数-gcflags=all=-l关闭Go语言的内联优化。
    • monkey不是线程安全的,所以不要把它用到并发的单元测试中。

    gomonkey 有多个版本,我们可以使用最新的v2版本

    github.com/agiledragon/gomonkey/v2
    
    mock全局变量
    var num = 10
    
    func TestApplyGlobalVar(t *testing.T) {
        patches := gomonkey.ApplyGlobalVar(&num, 150)
        defer patches.Reset()
    
        assert.Equal(t, num, 150)
    }
    
    mock 函数
    import (
        "github.com/agiledragon/gomonkey/v2"
        "github.com/stretchr/testify/assert"
        "testing"
    )
    
    func networkCompute(a, b int) (int, error) {
        // 假设这里依赖于其他远程服务
        c := a + b
        return c, nil
    }
    
    func Compute(a, b int) (int, error) {
        sum, err := networkCompute(a, b)
        return sum, err
    }
    
    func TestFunc(t *testing.T) {
        // mock networkCompute(),返回了计算结果2
        patches := gomonkey.ApplyFunc(networkCompute, func(a, b int) (int, error) {
            return 2, nil
        })
        // v2 还可以直接使用:
        //patches := gomonkey.ApplyFuncReturn(networkCompute, 2, nil)
    
        defer patches.Reset()
    
        sum, err := Compute(1, 2)
        assert.Equal(t, err, nil)
        assert.Equal(t, sum, 2)
    }
    

    这里有时候会打桩失败,原因是因为函数内联导致的。

    mock 方法
    type Task struct {
    }
    
    func (t *Task) Sub(a, b int) int {
        return a - b
    }
    
    func (t *Task) add(a, b int) int {
        return a + b
    }
    

    方法:

    func TestTask_Run(t *testing.T) {
        task := &Task{}
        patches := gomonkey.ApplyMethod(task, "Sub", func(a, b int) int {
            return a + b
        })
        //patches := gomonkey.ApplyMethodReturn(task, "Sub",4)
        defer patches.Reset()
    
        result := task.Sub(3, 2)
        assert.Equal(t, result, 5)
    }
    
    

    私有方法:

    func TestTask_add(t *testing.T) {
        task := &Task{}
        patches := gomonkey.ApplyPrivateMethod(task, "add", func(_ *Task, a, b int) int {
            return a * b
        })
        defer patches.Reset()
    
        result := task.add(3, 2)
        assert.Equal(t, result, 6)
    }
    

    参考资料
    1、https://www.jianshu.com/p/25d49af216b7

    相关文章

      网友评论

          本文标题:go 单元测试(二)mock

          本文链接:https://www.haomeiwen.com/subject/obspgrtx.html