xgo:一个基于 IR 重写的 golang mock 库 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
xhd2015
V2EX    Go 编程语言

xgo:一个基于 IR 重写的 golang mock 库

  •  1
     
  •   xhd2015 2024-03-28 09:18:54 +08:00 via iPhone 1814 次点击
    这是一个创建于 563 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近基于工作需要,在使用了 gomonkey 遇到很多问题之后,并且也担心基于 Assembly 的方式对未来单测维护的不可持续性,开发了 xgo 。

    xgo 基于 IR 中间码重写,更加贴近源代码而不是机器码。所以兼容性比 Assembly 要好很多,功能也更强。

    下面是一个简单的示例:

    package demo import ( "context" "testing" "github.com/xhd2015/xgo/runtime/core" "github.com/xhd2015/xgo/runtime/mock" ) func MyFunc() string { return "my func" } func TestFuncMock(t *testing.T) { mock.Mock(MyFunc, func(ctx context.Context, fn *core.FuncInfo, args core.Object, results core.Object) error { results.GetFieldIndex(0).Set("mock func") return nil }) text := MyFunc() if text != "mock func" { t.Fatalf("expect MyFunc() to be 'mock func', actual: %s", text) } } 

    欢迎大家使用和提意见

    项目地址 https://github.com/xhd2015/xgo

    12 条回复    2025-06-19 14:21:40 +08:00
    mightybruce
        1
    mightybruce  
       2024-03-28 10:51:56 +08:00   1
    行,关注了。
    xhd2015
        2
    xhd2015  
    OP
       2024-03-28 11:33:31 +08:00 via iPhone
    说明一下:该 mock 不需要基于接口进行 monkey ,开箱可用
    oceana
        3
    oceana  
       2024-03-28 12:59:10 +08:00   1
    点个赞
    xhd2015
        4
    xhd2015  
    OP
       2024-03-28 15:25:13 +08:00 via iPhone
    @oceana 感谢,可以一起交流
    sophos
        5
    sophos  
       2024-03-28 19:22:58 +08:00
    有点意思,和我这个项目的功能有点类似,不过实现思路不太一样 :-)

    https://github.com/go-kod/kod
    xhd2015
      &nbp; 6
    xhd2015  
    OP
       2024-03-28 19:27:07 +08:00 via iPhone
    @sophos 目标不一样,这个是单元测试场景,主打无代码侵入,无代码生成
    sophos
        7
    sophos  
       2024-03-28 19:48:22 +08:00
    @xhd2015 嗯,我这个是为了优化代码模块设计,同时解决单元测试标准化的问题

    不过个人感觉单元测试的最佳实践还是 mock interface ,patch 不够直观
    xhd2015
        8
    xhd2015  
    OP
       2024-03-28 20:01:32 +08:00 via iPhone
    @sophos 哈哈,我持相反的看法
    502Chef
        9
    502Chef  
       123 天前
    看文档是支持泛型函数指定类型 mock 的,但是自己尝试下来确不支持,求解答,谢谢

    报错:

    panic: func not instrumented by xgo, see https://github.com/xhd2015/xgo/tree/master/doc/ERR_NOT_INSTRUMENTED.md: demo_test.MyFuncT[int]



    ```go
    package demo_test

    import (
    "testing"

    "github.com/xhd2015/xgo/runtime/mock"
    )

    func MyFunc() string {
    return "my func"
    }

    func MyFuncT[T any](t T) T {
    return t
    }

    var MyFuncTInt = MyFuncT[int]

    func TestFuncMock(t *testing.T) {
    mock.Patch(MyFunc, func() string {
    return "mock func"
    })
    text := MyFunc()
    if text != "mock func" {
    t.Fatalf("expect MyFunc() to be 'mock func', actual: %s", text)
    }
    }

    func TestFuncMockIntA(t *testing.T) {
    mock.Patch(MyFuncTInt, func(t int) int {
    return 2
    })
    text := MyFuncTInt(1)
    if text != 2 {
    t.Fatalf("expect intA() to be '2', actual: %d", text)
    }
    }

    func TestFuncMockT(t *testing.T) {
    println("111111")
    mock.Patch(MyFuncTInt, func(t int) int {
    println("333333")
    return 2
    })
    println("222222")
    text := MyFuncTInt(1)
    if text != 2 {
    t.Fatalf("expect MyFuncT() to be '2', actual: %d", text)
    }
    }
    ```

    ```console
    xgo test -v ./
    === RUN TestFuncMock
    --- PASS: TestFuncMock (0.00s)
    === RUN TestFuncMockIntA
    --- FAIL: TestFuncMockIntA (0.00s)
    panic: func not instrumented by xgo, see https://github.com/xhd2015/xgo/tree/master/doc/ERR_NOT_INSTRUMENTED.md: demo_test.MyFuncT[int] [recovered]
    panic: func not instrumented by xgo, see https://github.com/xhd2015/xgo/tree/master/doc/ERR_NOT_INSTRUMENTED.md: demo_test.MyFuncT[int]

    goroutine 19 [running]:
    testing.tRunner.func1.2({0x100c85be0, 0x140000a0ac0})
    /Users/lula/.xgo/go-instrument/go1.24.2_op_ho_Ce_go_1._li_9f53f045/go1.24.2/src/testing/testing.go:1734 +0x1ac
    testing.tRunner.func1()
    /Users/lula/.xgo/go-instrument/go1.24.2_op_ho_Ce_go_1._li_9f53f045/go1.24.2/src/testing/testing.go:1737 +0x334
    panic({0x100c85be0?, 0x140000a0ac0?})
    /Users/lula/.xgo/go-instrument/go1.24.2_op_ho_Ce_go_1._li_9f53f045/go1.24.2/src/runtime/panic.go:792 +0x124
    github.com/xhd2015/xgo/runtime/internal/trap.Inspect({0x100c75480, 0x100cb6540})
    /Users/lula/Library/Go/pkg/mod/github.com/xhd2015/xgo/[email protected]/internal/trap/inspect.go:68 +0x530
    github.com/xhd2015/xgo/runtime/internal/trap.pushMockReplacer({0x100c75480, 0x100cb6540}, {0x100c75480, 0x100cb6550})
    /Users/lula/Library/Go/pkg/mod/github.com/xhd2015/xgo/[email protected]/internal/trap/mock.go:160 +0x3d0
    github.com/xhd2015/xgo/runtime/internal/trap.PushMockReplacer(...)
    /Users/lula/Library/Go/pkg/mod/github.com/xhd2015/xgo/[email protected]/internal/trap/mock.go:47
    github.com/xhd2015/xgo/runtime/mock.Patch(...)
    /Users/lula/Library/Go/pkg/mod/github.com/xhd2015/xgo/[email protected]/mock/patch.go:15
    demo_test.TestFuncMockIntA(0x14000146380)
    /Users/lula/Code/Go/go-learn/demo/demo_test.go:30 +0x14c
    testing.tRunner(0x14000146380, 0x100cb5c50)
    /Users/lula/.xgo/go-instrument/go1.24.2_op_ho_Ce_go_1._li_9f53f045/go1.24.2/src/testing/testing.go:1792 +0xe4
    created by testing.(*T).Run in goroutine 1
    /Users/lula/.xgo/go-instrument/go1.24.2_op_ho_Ce_go_1._li_9f53f045/go1.24.2/src/testing/testing.go:1851 +0x374
    FAIL demo 0.259s
    FAIL
    ```
    502Chef
        10
    502Chef  
       123 天前
    第三个 Test 函数代码贴错了,纠正一下,下面的日志没有错

    demo xgo version
    1.1.7
    demo xgo exec go version
    go version go1.24.2 darwin/arm64
    demo go env
    GOHOSTARCH='arm64'
    GOHOSTOS='darwin'
    GOVERSION='go1.24.2'

    ```go
    package demo_test

    import (
    "testing"

    "github.com/xhd2015/xgo/runtime/mock"
    )

    func MyFunc() string {
    return "my func"
    }

    func MyFuncT[T any](t T) T {
    return t
    }

    var MyFuncTInt = MyFuncT[int]

    func TestFuncMock(t *testing.T) {
    mock.Patch(MyFunc, func() string {
    return "mock func"
    })
    text := MyFunc()
    if text != "mock func" {
    t.Fatalf("expect MyFunc() to be 'mock func', actual: %s", text)
    }
    }

    func TestFuncMockIntA(t *testing.T) {
    mock.Patch(MyFuncTInt, func(t int) int {
    return 2
    })
    text := MyFuncTInt(1)
    if text != 2 {
    t.Fatalf("expect intA() to be '2', actual: %d", text)
    }
    }

    func TestFuncMockT(t *testing.T) {
    println("111111")
    mock.Patch(MyFuncT[int], func(t int) int {
    println("333333")
    return 2
    })
    println("222222")
    text := MyFuncTInt(1)
    if text != 2 {
    t.Fatalf("expect MyFuncT() to be '2', actual: %d", text)
    }
    }
    ```
    xhd2015
        11
    xhd2015  
    OP
       121 天前 via iPhone
    @huyujievip 感谢反馈,我今天看下这个问题。根据这个测试用例,范型是支持的 https://github.com/xhd2015/xgo/blob/master/runtime/test/mock/mock_generic/mock_generic_go1.20_above_should_success_test.go
    xhd2015
        12
    xhd2015  
    OP
       115 天前 via iPhone
    @huyujievip 第二个函数失败是因为 MyFuncTInt 是一个变量,而不是函数
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     852 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    VERSION: 3.9.8.5 26ms UTC 19:27 PVG 03:27 LAX 12:27 JFK 15:27
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86