使用Go testify包规避歧义调用时触发段错误的原因排查
问题描述
运行go test或go test -c时,单元测试代码在On和AssertExpectations方法处触发段错误,即使跳过问题代码行程序仍崩溃。测试代码如下:
type mockQux struct { mock.Mock MockInterface calculateHash func(ctx context.Context, foo *entities.Foo, bar model.Bar, baz *model.Baz) (uint64, error) } type MockInterface interface { On(methodName string, arguments ...interface{}) *mock.Call AssertExpectations func(t mock.TestingT) } func (m *mockQux) TestCalculateHash(t *testing.T) { mq := new(mockQux) ctx := context.Background() foo := &entities.Foo{ // foo's fields } // create instances of model.Bar and &model.Baz the same way I created foo variable mq.On("calculateHash", ctx, foo, bar, baz).Return(uint64(123), nil) hash, err := m.Mock.calculateHash(ctx, foo, bar, baz) assert.Equal(t, uint64(123), hash) assert.Nil(t, err) m.Mock.AssertExpectations(t) } func TestCalculateHash(t *testing.T) { m := new(mockQux) m.TestCalculateHash(t) }
怀疑问题出在mq.On的参数引用不当,或是未初始化所有结构体字段(结构体结构较为复杂)。
错误原因与修复方案
1. 重复嵌入导致的方法集混乱
mockQux同时嵌入了mock.Mock和MockInterface,但MockInterface定义的On、AssertExpectations方法已经由mock.Mock实现,重复嵌入会让结构体的方法集出现冲突,调用时实际指向未初始化的接口成员,直接触发空指针段错误。
修复: 删掉MockInterface嵌入,直接用mock.Mock自带的方法即可:
type mockQux struct { mock.Mock // 保留calculateHash字段,后续改为方法 calculateHash func(ctx context.Context, foo *entities.Foo, bar model.Bar, baz *model.Baz) (uint64, error) }
2. 函数字段未初始化+调用对象错误
你把calculateHash定义成了结构体字段,而且没初始化,直接调用m.Mock.calculateHash会触发空指针。另外测试里创建了mq实例,却去调用外层m.Mock的成员,完全没用到设置好预期的mq。
修复: 给mockQux实现calculateHash方法,内部用mock.Mock的Called方法匹配预期:
func (m *mockQux) calculateHash(ctx context.Context, foo *entities.Foo, bar model.Bar, baz *model.Baz) (uint64, error) { args := m.Called(ctx, foo, bar, baz) return args.Get(0).(uint64), args.Error(1) }
同时修改测试逻辑,用创建好的mq实例调用方法并验证预期:
func (m *mockQux) TestCalculateHash(t *testing.T) { mq := new(mockQux) ctx := context.Background() foo := &entities.Foo{/* 补全字段初始化 */} bar := model.Bar{/* 补全字段初始化 */} baz := &model.Baz{/* 补全字段初始化 */} mq.On("calculateHash", ctx, foo, bar, baz).Return(uint64(123), nil) hash, err := mq.calculateHash(ctx, foo, bar, baz) assert.Equal(t, uint64(123), hash) assert.Nil(t, err) mq.AssertExpectations(t) }
3. 未初始化的变量问题
代码里bar和baz只写了注释但没实际初始化,如果model.Bar是值类型,未初始化的无效数据会导致预期匹配出错,甚至触发崩溃。必须确保传入On的所有参数都完成正确初始化。
4. 冗余的测试结构
外层TestCalculateHash函数创建实例后调用结构体的同名方法,这种嵌套完全没必要,直接把测试逻辑写在顶层测试函数里更清晰:
func TestCalculateHash(t *testing.T) { mq := new(mockQux) ctx := context.Background() foo := &entities.Foo{/* 初始化 */} bar := model.Bar{/* 初始化 */} baz := &model.Baz{/* 初始化 */} mq.On("calculateHash", ctx, foo, bar, baz).Return(uint64(123), nil) hash, err := mq.calculateHash(ctx, foo, bar, baz) assert.Equal(t, uint64(123), hash) assert.Nil(t, err) mq.AssertExpectations(t) }
内容的提问来源于stack exchange,提问作者olithad
相关产品推荐
相关产品推荐

