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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 12:15:33