gomonkey用户如何对桩计数

问题的由来

在 gomonkey 社区,用户 JiangHanChao 提了一个 issue,如下图所示:

issue-82.png

恰巧,笔者身边的同事和朋友也有咨询过该问题,因此就想着写篇短文来统一回复。

解决方案

gomonkey 在实现打桩功能时,仅在函数头植入了几行汇编,再增加一个计数功能的话,对某些简短的函数打桩后会破坏下一个函数的序言,进而造成运行时段错误,并且这个错误没法在打桩时就发现并告知用户。

基于上述考虑,gomonkey 对桩计数不能在框架层面统一解决,可以考虑在用户侧来解决,即在测试代码中定义桩计数器结构体,每个测试开始时初始化该结构体,每个测试结束前断言该结构体中的字段。

典型案例

将 issue-82 中给出的示例作为典型案例,供大家参考!

(1)产品代码保持不变

package moduleA

func FuncA() error {
    return nil
}

func FuncB() error {
    return nil
}

func FuncC() error {
    if err := FuncA(); err != nil {
        return err
    }

    if err := FuncB(); err != nil {
        return err
    }

    return nil
}

(2)在测试代码中应用本文解决方案

package moduleA

import (
    "errors"
    "testing"

    . "github.com/agiledragon/gomonkey/v2"
    . "github.com/smartystreets/goconvey/convey"
)

// 在测试代码中定义桩计数器结构体
type monkeyMetric struct {
    callFuncATimes int
    callFuncBTimes int
}

func TestFuncC(t *testing.T) {
    // 每个测试开始时初始化该结构体
    var metric monkeyMetric
    err := errors.New("test")
    Convey("test", t, func() {
        patches := ApplyFunc(FuncA, func() error {
            metric.callFuncATimes++
            return err
        })
        patches.ApplyFunc(FuncB, func() error {
            metric.callFuncBTimes++
            return err
        })
        defer patches.Reset()

        e := FuncC()
        So(e, ShouldEqual, err)
        // 每个测试结束前断言该结构体中的字段
        So(metric.callFuncATimes, ShouldEqual, 1)
        So(metric.callFuncBTimes, ShouldEqual, 1)
    })
}

(3)执行该测试用例,结果符合预期
测试用例执行结果:

=== RUN   TestFuncC

  test ✔✔✘


Failures:

  * /Users/zhangxiaolong/Desktop/D/go-workspace/gomonkey/test/module_a_test.go
  Line 33:
  Expected: '1'
  Actual:   '0'
  (Should be equal)

用户在 issue-82 中的期望:

user-expect.png

小结

用户期望可以在框架层面解决一切共性问题,这本属于合理的诉求,但现实有时很骨感,在种种约束下必须改变思路,而在用户侧较优雅的解决相关问题也是一种不错的选择。

本文针对 gomonkey 用户,给出了如何对桩进行计数的解决方案,并提供了典型案例,希望对读者有一定的帮助!

你可能感兴趣的:(gomonkey用户如何对桩计数)