golang / mock

GoMock is a mocking framework for the Go programming language.
Apache License 2.0
9.26k stars 608 forks source link

Type-safe return values #622

Open trim21 opened 2 years ago

trim21 commented 2 years ago

Requested feature

This issue proposal generating a typed .Return( for *gomock.Call.

it's a re-open for closed issue #427 .

Why the feature is needed

currently .Return( has a sigurate of func (rets ...interface{}) can accept any return value in compile time.

But wrong return values' type result in always failing at runtime, which can be avoided at compile time.

Proposed solution

User Code

type Auth interface {
    // GetByToken return an authorized user by a valid access token.
    GetByToken(ctx context.Context, token string) (model.User, error)
}

Generated mocks

// Code generated by MockGen. DO NOT EDIT.

type MockAuthMockRecorder struct {
    ...
}

// GetByToken indicates an expected call of GetByToken.
func (mr *MockAuthMockRecorder) GetByToken(ctx, token interface{}) *TypedCall {
    mr.mock.ctrl.T.Helper()
    return &TypedCall{mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByToken",
        reflect.TypeOf((*MockAuth)(nil).GetByToken), ctx, token)}
}

type TypedCall struct {
    *gomock.Call
}

func (t *TypedCall) Return(typedReturn1 model.User, typedReturn2 error) *gomock.Call {
    return t.Call.Return(typedReturn1, typedReturn2)
}

User Test Code

func TestTypeSafeReturn(t *testing.T) {
    m := NewMockAuth(gomock.NewController(t))
    m.GetByToken(gomock.Any(), gomock.Any()).Return() <== Now we get compile time error

    // These should works fine at compile time and runtime.
    m.GetByToken(gomock.Any(), gomock.Any()).Return(model.User{}, nil)
    m.GetByToken(gomock.Any(), gomock.Any()).Return(model.User{}, errors.New("Not Found")) 
}
codyoss commented 2 years ago

The reason this suggestion was closed in the path is it sounded like it would be a breaking change. Looking at this prop that is also true. I think if we were going to do something like this in the future we would want to do it in a way that is backwards compatible. Do you have a suggested API that is not a breaking change?

trim21 commented 2 years ago

The code will break with this change it already broken at runtime (always panic). This change only make then break early to compile time.

non-breaking-change Suggestions:

  1. Add a flag in cli that generate new typed call.
  2. Add a .TypedReturn( , .Typed().Return( or new struct NewTypedMockAuth(gomock.NewController(t)) these methods are type-safe.

Personally I prefer first one.

trim21 commented 2 years ago

@codyoss any thoughts on these suggestions?

trim21 commented 1 year ago

Another mock generator support typed-return

https://github.com/vektra/mockery

And it's kinda funny because it doesn't support type safe matcher...

codyoss commented 1 year ago

I don't disagree that overall type-safe returns would be a good idea, but there are potentially people exploiting the current state that may be broken. Let me think about this some more and see if this issue gets more traction. The flag suggestion seems like an okay idea though.

alvarotuso commented 1 year ago

This would also be very interesting for me 👍 . Not having typed returns makes test maintenance harder.