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
相关产品推荐
相关产品推荐

