Closed pavleprica closed 1 month ago
Hi @pavleprica . This has already been implemented and is working.
type Storage[V any] interface {
Set(ctx context.Context, item V, rowVersion internal.RowVersion) error
Delete(ctx context.Context, item V, rowVersion internal.RowVersion) error
}
...
// MockStorage is a mock of Storage interface.
type MockStorage[V any] struct {
ctrl *gomock.Controller
recorder *MockStorageMockRecorder[V]
}
...
// Set mocks base method.
func (m *MockStorage[V]) Set(ctx context.Context, item V, rowVersion internal.RowVersion) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Set", ctx, item, rowVersion)
ret0, _ := ret[0].(error)
return ret0
}
...
Hey @tulzke, Thank you for your response!
But this doesn't seem like handling of generics by gomock
? Perhaps I'm just not seeing the whole picture.
But this is a "custom" implementation of the interface to mock it "manually". Not like you can rely on the .EXPECT()
methods.
Or the example you provided is from the generated code?
I'm coming across a similar problem I think, I'm trying to create a mock for this interface (a connectrpc handler interface generated from protos):
type ServiceHandler interface {
Set(context.Context, *connect.Request[package.SetRequest]) (*connect.Response[package.SetResponse], error)
}
mockgen
errors with:
2023/12/18 11:58:16 Failed to format generated source code: cachetest/handler.go:40:84: missing ',' in type argument list (and 5 more errors)
The generated code included with the error:
// Code generated by MockGen. DO NOT EDIT.
// Source: REDACTED
//
// Generated by this command:
// mockgen github.com/repo/path/package/packageconnect ServiceHandler
// Package cachetest is a generated GoMock package.
package cachetest
import (
reflect "reflect"
connect "connectrpc.com/connect"
context "context"
gomock "go.uber.org/mock/gomock"
)
// MockHandler is a mock of Handler interface.
type MockHandler struct {
ctrl *gomock.Controller
recorder *MockHandlerMockRecorder
}
// MockHandlerMockRecorder is the mock recorder for MockHandler.
type MockHandlerMockRecorder struct {
mock *MockHandler
}
// NewMockHandler creates a new mock instance.
func NewMockHandler(ctrl *gomock.Controller) *MockHandler {
mock := &MockHandler{ctrl: ctrl}
mock.recorder = &MockHandlerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockHandler) EXPECT() *MockHandlerMockRecorder {
return m.recorder
}
// Set mocks base method.
func (m *MockHandler) Set(arg0 context.Context, arg1 *connect.Request[github.com/repo/path/package.SetRequest]) (*connect.Response[github.com/repo/path/package.SetResponse], error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Set", arg0, arg1)
ret0, _ := ret[0].(*connect.Response[github.com/repo/path/package.SetResponse])
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Set indicates an expected call of Set.
func (mr *MockHandlerMockRecorder) Set(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockHandler)(nil).Set), arg0, arg1)
}
As you can see from the generated code it doesn't look like mockgen
can resolve the generic type imports correctly and we get invalid code.
I executed mockgen --source=ping.connect.go --destination=mock/ping.mock.go --package mock
command in github.com/connectrpc/connect-go/internal/gen/connect/ping/v1/pingv1connect
, and the result was
// Code generated by MockGen. DO NOT EDIT.
// Source: ping.connect.go
//
// Generated by this command:
//
// mockgen --source=ping.connect.go --destination=mock/ping.mock.go --package mock
//
// Package mock is a generated GoMock package.
package mock
import (
context "context"
reflect "reflect"
connect "connectrpc.com/connect"
pingv1 "connectrpc.com/connect/internal/gen/connect/ping/v1"
gomock "go.uber.org/mock/gomock"
)
// MockPingServiceClient is a mock of PingServiceClient interface.
type MockPingServiceClient struct {
ctrl *gomock.Controller
recorder *MockPingServiceClientMockRecorder
}
// MockPingServiceClientMockRecorder is the mock recorder for MockPingServiceClient.
type MockPingServiceClientMockRecorder struct {
mock *MockPingServiceClient
}
// NewMockPingServiceClient creates a new mock instance.
func NewMockPingServiceClient(ctrl *gomock.Controller) *MockPingServiceClient {
mock := &MockPingServiceClient{ctrl: ctrl}
mock.recorder = &MockPingServiceClientMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockPingServiceClient) EXPECT() *MockPingServiceClientMockRecorder {
return m.recorder
}
// CountUp mocks base method.
func (m *MockPingServiceClient) CountUp(arg0 context.Context, arg1 *connect.Request[pingv1.CountUpRequest]) (*connect.ServerStreamForClient[pingv1.CountUpResponse], error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CountUp", arg0, arg1)
ret0, _ := ret[0].(*connect.ServerStreamForClient[pingv1.CountUpResponse])
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CountUp indicates an expected call of CountUp.
func (mr *MockPingServiceClientMockRecorder) CountUp(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountUp", reflect.TypeOf((*MockPingServiceClient)(nil).CountUp), arg0, arg1)
}
// CumSum mocks base method.
func (m *MockPingServiceClient) CumSum(arg0 context.Context) *connect.BidiStreamForClient[pingv1.CumSumRequest, pingv1.CumSumResponse] {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CumSum", arg0)
ret0, _ := ret[0].(*connect.BidiStreamForClient[pingv1.CumSumRequest, pingv1.CumSumResponse])
return ret0
}
// CumSum indicates an expected call of CumSum.
func (mr *MockPingServiceClientMockRecorder) CumSum(arg0 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CumSum", reflect.TypeOf((*MockPingServiceClient)(nil).CumSum), arg0)
}
// Fail mocks base method.
func (m *MockPingServiceClient) Fail(arg0 context.Context, arg1 *connect.Request[pingv1.FailRequest]) (*connect.Response[pingv1.FailResponse], error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Fail", arg0, arg1)
ret0, _ := ret[0].(*connect.Response[pingv1.FailResponse])
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Fail indicates an expected call of Fail.
func (mr *MockPingServiceClientMockRecorder) Fail(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Fail", reflect.TypeOf((*MockPingServiceClient)(nil).Fail), arg0, arg1)
}
// Ping mocks base method.
func (m *MockPingServiceClient) Ping(arg0 context.Context, arg1 *connect.Request[pingv1.PingRequest]) (*connect.Response[pingv1.PingResponse], error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Ping", arg0, arg1)
ret0, _ := ret[0].(*connect.Response[pingv1.PingResponse])
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Ping indicates an expected call of Ping.
func (mr *MockPingServiceClientMockRecorder) Ping(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockPingServiceClient)(nil).Ping), arg0, arg1)
}
// Sum mocks base method.
func (m *MockPingServiceClient) Sum(arg0 context.Context) *connect.ClientStreamForClient[pingv1.SumRequest, pingv1.SumResponse] {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Sum", arg0)
ret0, _ := ret[0].(*connect.ClientStreamForClient[pingv1.SumRequest, pingv1.SumResponse])
return ret0
}
// Sum indicates an expected call of Sum.
func (mr *MockPingServiceClientMockRecorder) Sum(arg0 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sum", reflect.TypeOf((*MockPingServiceClient)(nil).Sum), arg0)
}
// MockPingServiceHandler is a mock of PingServiceHandler interface.
type MockPingServiceHandler struct {
ctrl *gomock.Controller
recorder *MockPingServiceHandlerMockRecorder
}
// MockPingServiceHandlerMockRecorder is the mock recorder for MockPingServiceHandler.
type MockPingServiceHandlerMockRecorder struct {
mock *MockPingServiceHandler
}
// NewMockPingServiceHandler creates a new mock instance.
func NewMockPingServiceHandler(ctrl *gomock.Controller) *MockPingServiceHandler {
mock := &MockPingServiceHandler{ctrl: ctrl}
mock.recorder = &MockPingServiceHandlerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockPingServiceHandler) EXPECT() *MockPingServiceHandlerMockRecorder {
return m.recorder
}
// CountUp mocks base method.
func (m *MockPingServiceHandler) CountUp(arg0 context.Context, arg1 *connect.Request[pingv1.CountUpRequest], arg2 *connect.ServerStream[pingv1.CountUpResponse]) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CountUp", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// CountUp indicates an expected call of CountUp.
func (mr *MockPingServiceHandlerMockRecorder) CountUp(arg0, arg1, arg2 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountUp", reflect.TypeOf((*MockPingServiceHandler)(nil).CountUp), arg0, arg1, arg2)
}
// CumSum mocks base method.
func (m *MockPingServiceHandler) CumSum(arg0 context.Context, arg1 *connect.BidiStream[pingv1.CumSumRequest, pingv1.CumSumResponse]) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CumSum", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// CumSum indicates an expected call of CumSum.
func (mr *MockPingServiceHandlerMockRecorder) CumSum(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CumSum", reflect.TypeOf((*MockPingServiceHandler)(nil).CumSum), arg0, arg1)
}
// Fail mocks base method.
func (m *MockPingServiceHandler) Fail(arg0 context.Context, arg1 *connect.Request[pingv1.FailRequest]) (*connect.Response[pingv1.FailResponse], error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Fail", arg0, arg1)
ret0, _ := ret[0].(*connect.Response[pingv1.FailResponse])
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Fail indicates an expected call of Fail.
func (mr *MockPingServiceHandlerMockRecorder) Fail(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Fail", reflect.TypeOf((*MockPingServiceHandler)(nil).Fail), arg0, arg1)
}
// Ping mocks base method.
func (m *MockPingServiceHandler) Ping(arg0 context.Context, arg1 *connect.Request[pingv1.PingRequest]) (*connect.Response[pingv1.PingResponse], error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Ping", arg0, arg1)
ret0, _ := ret[0].(*connect.Response[pingv1.PingResponse])
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Ping indicates an expected call of Ping.
func (mr *MockPingServiceHandlerMockRecorder) Ping(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockPingServiceHandler)(nil).Ping), arg0, arg1)
}
// Sum mocks base method.
func (m *MockPingServiceHandler) Sum(arg0 context.Context, arg1 *connect.ClientStream[pingv1.SumRequest]) (*connect.Response[pingv1.SumResponse], error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Sum", arg0, arg1)
ret0, _ := ret[0].(*connect.Response[pingv1.SumResponse])
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Sum indicates an expected call of Sum.
func (mr *MockPingServiceHandlerMockRecorder) Sum(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sum", reflect.TypeOf((*MockPingServiceHandler)(nil).Sum), arg0, arg1)
}
Hey there @tra4less,
I generally replicated the issue from the archived repository. I ran into it, while running into my own problem, which I presume might be related to the one.
But, to replicate my issue, you can use this code. A bit simplified, but should do the trick
Seeing exactly the same issue. In my case, the generated code has full package/module paths in the generics qualifiers:
// Code generated by MockGen. DO NOT EDIT.
// Source: golang.linkedin.com/config-publish-api/config-publish-api-go/generated/com/linkedin/config/compiledConfigDescriptors (interfaces: Client)
...
func (m *MockClient) Create(arg0 *config.CompiledConfigDescriptor) (*common.CreatedEntity[*golang.linkedin.com/config-publish-api/config-publish-api-go/generated/com/linkedin/config/compiledConfigDescriptors.CompiledConfigDescriptors_ComplexKey], error) {
...
func (m *MockClient) CreateWithContext(arg0 context.Context, arg1 *config.CompiledConfigDescriptor) (*common.CreatedEntity[*golang.linkedin.com/config-publish-api/config-publish-api-go/generated/com/linkedin/config/compiledConfigDescriptors.CompiledConfigDescriptors_ComplexKey], error) {
which looks like obvious syntax errors.
Seeing the same thing
Same thing, syntax error on generating mocks for interfaces with embedded generics.
My issues were the same as described by @krak3n and @ahmetb. I'm using connectrpc and saw the full module names in the generated code, creating the syntax errors.
But @tra4less's solution of using mockgen
source mode worked for me. I ran this command:
mockgen \
-source=./pkg/proto/life/v1/lifev1connect/life.connect.go \
-destination=./pkg/subsystem/mocks/mock_subsystem_service.go \
-package=mocks github.com/jonnylangefeld/life/pkg/proto/life/v1/lifev1connect \
SubsystemServiceClient
The generated code now had an import like this:
lifev1 "github.com/jonnylangefeld/life/pkg/proto/life/v1"
And the endpoints correctly used the named import in the generics code:
// SayHello mocks base method.
func (m *MockHelloServiceClient) SayHello(arg0 context.Context, arg1 *connect.Request[lifev1.SayHelloRequest]) (*connect.Response[lifev1.SayHelloResponse], error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SayHello", arg0, arg1)
ret0, _ := ret[0].(*connect.Response[lifev1.SayHelloResponse])
ret1, _ := ret[1].(error)
return ret0, ret1
}
Also ran into this issue in which mockgen
source mode doesn't help, here's a slim repro: https://play.golang.com/p/VPmHL06LTPH
generated code is:
// Set mocks base method.
func (m *MockSample) Set(arg0 context.Context, arg1 serializable) error {
when it should be:
// Set mocks base method.
func (m *MockSample) Set(arg0 context.Context, arg1 serializable[any]) error {
Hey team,
Hope you are doing well. Stumbled upon on this issue. In code as well as on the archived repo. To not copy paste too much, is this perhaps on the roadmap for you?
It seems that the previous team was on it, but didn't got to the latest release including it.