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 · 1571 次点击
    这是一个创建于 442 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近基于工作需要,在使用了 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

    11 条回复    2025-06-13 07:44:14 +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
        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 哈哈,我持相反的看法
    huyujievip
        9
    huyujievip  
       2 天前
    看文档是支持泛型函数指定类型 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
    ```
    huyujievip
        10
    huyujievip  
       2 天前
    第三个 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
       16 小时 27 分钟前 via iPhone
    @huyujievip 感谢反馈,我今天看下这个问题。根据这个测试用例,范型是支持的 https://github.com/xhd2015/xgo/blob/master/runtime/test/mock/mock_generic/mock_generic_go1.20_above_should_success_test.go
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2140 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 16:12 · PVG 00:12 · LAX 09:12 · JFK 12:12
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.