Go testify package: segfault when trying to avoid ambiguous call

106 Views Asked by At

I am writing the following unit test:

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)
}

Running go test and go test -c, why do I get a segfault on On and AssertExpectations?

I've tried debugging this with delve, but it crashes whenever I run it, even if I instruct it to step over the problematic lines. I guess the problem might be that I improperly reference the variables in arguments to mq.On or that I don't initialize all the fields (these structs are pretty complex).

1

There are 1 best solutions below

0
davidriod On

There are too many fail point in your code sample to tell what's the exact root cause of the segfault. That's generally not how testify mock are used. You don't need either your MockInterface definition. Also you cannot have a segfault on 2 different locations, your program either segfault on On or AssertExpectations. That cannot be both. I strongly suspect though that your segfault come from this call:

hash, err := m.Mock.calculateHash(ctx, foo, bar, baz)

as this function pointer from your struct does not seem to be initialized.

About how to use mock in testify, we usually do something like that. Here is a minimal nominal code without any business logic:

type ConcreteType struct {}
func (m *ConcreteType) DoSomething() int { return 3 }

type DoSomethinger interface {
    DoSomething() int
}

func UseConcreteType(s DoSomethinger) error {
   if val := s.DoSomething(); val != 3 {
       return fmt.Errorf("unexpected value: %d", val)
   }
}

In your test you would do something like that:

type DoSomethingMock struct {
    mock.Mock
}

func (m *DoSomethingMock) DoSomething() int {
    args := m.Called()
    return args.Int(0)
}

func TestUseConcrete(t *testing.T) {
   m := &DoSomethingMock{}
   m.On("DoSomething").Return(4)
   err := UseConcreteType(m)
   require.Error(t, err)
}