You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang中如何对go-plugin的相关函数执行Mock操作?

解决方案

核心思路

通过抽象两层依赖(plugin.Open函数、*plugin.Plugin结构体的Lookup方法),完全解耦业务代码和原生plugin包的实现,即可实现全链路Mock。

1. 定义抽象接口

首先定义接口抽象原生plugin.Plugin的行为,再基于该接口定义Open函数的类型:

import "plugin"

// Plugin 抽象原生plugin.Plugin的行为
type Plugin interface {
    Lookup(symName string) (plugin.Symbol, error)
}

// PluginOpenFunc 抽象plugin.Open函数的类型
type PluginOpenFunc func(path string) (Plugin, error)

2. 实现生产环境默认适配

原生*plugin.Plugin已经自动实现了上述Plugin接口,直接做一层简单包装即可作为生产环境的默认实现:

// DefaultPluginOpen 生产环境用的原生plugin.Open包装
func DefaultPluginOpen(path string) (Plugin, error) {
    return plugin.Open(path)
}

3. 改写业务函数

保留对外无侵入的调用方式,内部用注入的抽象依赖实现逻辑:

import "path"

// 对外暴露的生产用方法,无额外参数,兼容原有调用逻辑
func TestCheckPluginFile(fName string) error {
    return testCheckPluginFile(fName, DefaultPluginOpen)
}

// 内部可注入的实现,用于单元测试
func testCheckPluginFile(fName string, openFunc PluginOpenFunc) error {
    p, err := openFunc(path.Join("/I/expect/folder/", "/plugin-lib-test/"+fName))
    if err != nil {
        return err
    }
    _, err = p.Lookup("symbol")
    return err
}

4. 编写Mock实现和测试用例

自定义Mock结构体实现Plugin接口,即可完全控制Lookup方法的返回逻辑:

import "errors"
import "testing"

// MockPlugin 测试用的Mock实现
type MockPlugin struct {
    // 自定义回调控制Lookup返回
    LookupFunc func(symName string) (plugin.Symbol, error)
}

func (m *MockPlugin) Lookup(symName string) (plugin.Symbol, error) {
    return m.LookupFunc(symName)
}

// 示例测试:模拟Lookup返回错误的场景
func TestTestCheckPluginFile_LookupFail(t *testing.T) {
    // 自定义注入的Open函数
    mockOpen := func(path string) (Plugin, error) {
        // 可先校验传入路径是否符合预期
        expectPath := "/I/expect/folder/plugin-lib-test/test.so"
        if path != expectPath {
            t.Fatalf("expect path %s, got %s", expectPath, path)
        }
        // 返回MockPlugin实例
        return &MockPlugin{
            LookupFunc: func(symName string) (plugin.Symbol, error) {
                // 校验查找的符号是否符合预期
                if symName != "symbol" {
                    t.Fatalf("expect symName 'symbol', got %s", symName)
                }
                // 模拟Lookup失败
                return nil, errors.New("mock lookup error")
            },
        }, nil
    }

    // 调用测试方法,注入自定义的mockOpen
    err := testCheckPluginFile("test.so", mockOpen)
    if err == nil || err.Error() != "mock lookup error" {
        t.Fatalf("expect lookup error, got %v", err)
    }
}

其他可选方案说明

不推荐使用Go猴子补丁类的方案,这类方案依赖运行时代码修改,兼容性差,在开启编译优化、跨平台场景下容易失效,接口抽象是最符合Go语言设计规范的稳定方案。


内容的提问来源于stack exchange,提问作者Harrien

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 23:15:09