如何在Go单元测试中Mock嵌套结构体的方法?
解决方案
由于你无法修改原有结构体和方法的代码,而OuterThing的innerThing字段是具体类型*InnerThing而非接口,常规的接口依赖注入方式不适用。以下两种方法可以实现Mock需求:
方法1:使用方法Patch库(推荐)
借助go-mpatch这类库可以直接替换目标方法的实现,无需改动原代码,是最稳妥的方案。
步骤:
- 安装依赖:
go get github.com/undefinedlabs/go-mpatch
- 编写测试用例:
import ( "errors" "testing" "github.com/undefinedlabs/go-mpatch" ) func TestDoLotsOfStuff(t *testing.T) { testCases := []struct { name string mockHandler func(*InnerThing, string) (uint64, error) expectErr bool }{ { name: "happy case", mockHandler: func(_ *InnerThing, _ string) (uint64, error) { return 123, nil }, expectErr: false, }, { name: "error case", mockHandler: func(_ *InnerThing, _ string) (uint64, error) { return 0, errors.New("mock error") }, expectErr: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Patch InnerThing.DoStuff方法,替换为mock逻辑 patch, err := mpatch.PatchMethod((*InnerThing).DoStuff, tc.mockHandler) if err != nil { t.Fatalf("patch failed: %v", err) } defer patch.Unpatch() // 测试结束后恢复原方法 // 初始化测试对象 outer := &OuterThing{ innerThing: &InnerThing{name: "test"}, } outer.doLotsOfStuff() // 这里添加你的断言逻辑,比如验证错误分支是否执行 }) } }
方法2:使用unsafe包(不推荐)
利用unsafe包转换指针类型,将Mock实例伪装成*InnerThing。这种方式依赖Go的内存布局,存在版本兼容性风险,仅适合临时测试场景。
步骤:
- 定义Mock结构体(借助
testify/mock简化Mock逻辑):
import "github.com/stretchr/testify/mock" type MockInnerThing struct { mock.Mock } func (m *MockInnerThing) DoStuff(x string) (uint64, error) { args := m.Called(x) return args.Get(0).(uint64), args.Error(1) }
- 编写测试用例:
import ( "errors" "testing" "unsafe" "github.com/stretchr/testify/mock" ) func TestDoLotsOfStuff_Unsafe(t *testing.T) { t.Run("error case", func(t *testing.T) { // 初始化Mock并设置预期 mockInner := new(MockInnerThing) mockInner.On("DoStuff", "lots of stuff").Return(uint64(0), errors.New("mock error")) // 用unsafe转换指针类型,将Mock实例赋值给innerThing outer := &OuterThing{ innerThing: (*InnerThing)(unsafe.Pointer(mockInner)), } outer.doLotsOfStuff() // 验证Mock的预期是否被调用 mockInner.AssertExpectations(t) // 添加其他业务断言 }) }
关于场景的说明
这种需求并不罕见,Go的设计理念更偏向依赖接口而非具体类型,所以在业务代码初期定义接口能大幅降低Mock难度。但面对无法修改的遗留代码,方法Patch类库是更可靠的选择。
内容的提问来源于stack exchange,提问作者kiltek
相关产品推荐
相关产品推荐

