-
Notifications
You must be signed in to change notification settings - Fork 607
Mockgen: Support generating mock for interfaces with generics #621
Comments
In fact, I've gotten errors generating mocks for interfaces that don't use generics but are located in packages that do use generics. So I think this might be a slightly more serious issue than it appears at first glance. |
Hey, this is planned and will try to land as a fast followup to 1.18. Thank you for the request! |
Do you have a rough idea of when we can expect a generics compatible release? This is the main impediment for us to aggressively adopt generic goodness. |
FWIW I was able to work around the above issue by moving interfaces out to their own file that don't use generics. That doesn't help for cases where you want the interface itself to use generics, but it did help for my case. |
Haven't looked into Go generics yet, but shouldn't it be possible to rewrite mock with generics and remove the need to generate mocks code? I imagine it could be similar to C# moq. service := gomock.New[MyServiceInterface]()
service.EXPECT().GetItem(gomock.Any).Returns(item, nil) |
I don't believe the above is possible with go generics. The code has no way of reflecting on the type of the actual parameter. @codyoss do you have an idea of when we can expect generics support? |
Looks like it's possible to work around this issue by using type aliases: // Broken:
type Target interface {
Method() *generic.Type[int]
}
// Working:
type GenericTypeInt = generic.Type[int]
type Target interface {
Method() *GenericTypeInt
} |
@powerman |
@codyoss Now that Go 1.18 has been out for over a month, is there a timeline update you can share? Thank you! |
The solution proposed by @powerman doesn't work for generics working with custom types from what I can see:
What I see happening is that the generated output is like this for the generic return parameter: |
At a glance there is a typo in your example code, this line probably should be: type GenericTypeGenThing = foo.Gen[*Thing] // Added "foo." here. Unlikely this is the actual problem, because without "foo." it shouldn't compile at all. Anyway, working example in some temp repo would be useful to understand why workaround doesn't works for you. |
Yeah, that's a typo but not the issue. As you said, it wouldn't have compiled if that were the issue. That's what I get for hastily making a contrived example based on the real one 😝 I appreciate your looking into it! |
@powerman I have set up a repository with reproduction of a couple different issues with generics and |
I'm looking forward for this. |
Thanks for all the good discussion here folks. I have some time set aside this week to try to implement something here. I will likely put this out in a beta release as I want to make sure this change lands well. Thanks for the patience. |
Caveat: This post is not official by any means!TL;DR: I added support for generating mocks with generics to my
|
Hey, if anyone wants to locally checkout my draft PR and try to generate some generic mocks in source mode that would be great! I would like to to see if I need some more test cases 😄 I plan to iterate on reflect mode in a separate PR |
@codyoss thank you very much! i will do some tests tomorrow morning 🚀 |
@codyoss I tried your
I'm trying to generate a mock for an interface which uses generic structs: // service.go
type Service interface {
Create(ctx context.Context, params Params, input Input) error
Update(ctx context.Context, params Params, input Input) error
List(ctx context.Context, params Params, filters Filters) (*controller.Results[Output], error)
Fetch(ctx context.Context, params Params) (*controller.Result[Output], error)
Delete(ctx context.Context, params Params) error
} In the case above my generic struct is Using specifically the commit 90e3327ab5fb3e24df8dc48272b1c2e11a9d86d0 it panics with:
|
@herlon214 Thank you for taking the time to try out the branch. I think you may have missed a step in your testing perhaps? I added two more test cases that should cover your use case and it worked for me. To test make sure you:
Can you confirm that you rebuilt mockgen, I suspect that that is why you might be getting that error. |
@codyoss here are the steps I did:
Couldn't finish the step Did I miss anything? |
@herlon214 What you did looks good, I think I see what the issue is now. I will try to get a patch out later today for this use case. Thank you for the report! |
@herlon214 I think the issue you were seeing now should be fixed. Had not accounted for generic structs that don't use type params from the interface. |
@codyoss looks like it worked now! |
Hey @codyoss I found another issue. I have a type which aliases one generic type:
The generated mocks for that are:
|
Sounds good - will try to test soon. Thanks for all your efforts on this @codyoss 😃 |
I went ahead and created a new issue for this - there is still an issue with type annotations across packages. I have a fairly thorough issue repo set up at https://github.com/bradleygore/gomock-generics-issue and the issue I just wrote up is at #643 . Again - thanks for all efforts on this front! |
I also faced another issue with Generics, in case I use a concrete type:
When running
Also tried using
Result in:
|
It would appear that using gomock Sourcepkg/connection/connection.go
pkg/model/model.go
pkg/testservice/sometestservice.go
Command
Output
Update: I believe this is related to #643 |
Looked more into reflect mode and I don't think implementing generics support for reflect mode will work. Reflect works with runtime types and by just looking at an interface with constraints there is no way to get these runtime types. At least for the initial release of support generics will only be supported in source mode. If I overlooked something here please feel free to correct me. For now I am closing out this issue as initial support has landed on HEAD. I plan on releasing a beta tag of this next week. |
@DanielSolomon Sorry I missed that, would you mind busting that out into a separate issue? I would like to treat any new requests for how things work here as bugs in the current impl. It will also help better track the discussion. |
Hi guys. It works great. Thanks for your effort. When do you think there will be a release named |
Hey, guys! So where're we with support for embedded generic interfaces? Tried mockgen v1.7.0-rc.1 - no luck. If anyone can help or point the right direction would appreciate it! |
Some standard struct or interface was very useful like: type Storage[K comparable, V any] interface {
Get(K) V
Set(K, V)
Del(K)
}
type node[V any] struct {
left, right *node[V]
} I think no more alias in needed. |
This PR addresses two major issues * `mockgen` cannot yet mock higher-level wrappers, like `*AndWait` because of generics are not yet supported by gomock: golang/mock#621 * fundamentally fixes #87 # Interoperability with `gomock` When developing large applications, you find yourself in need of mocking APIs. For Go, there's [`gomock`](https://github.com/golang/mock) framework for code generating testing mocks. In this small example, we'll show how to use `gomock` with Databricks SDK for Go. Please read through [`dbfs_test.go`](https://github.com/databricks/databricks-sdk-go/pull/131/files#diff-ac4ba8227927778705ac100663a6f04a314e590b0d0f19c1699fe63ae62b801e) test example. ## Declaring which mocks to generate ```go //go:generate go run github.com/golang/mock/mockgen@latest -package=mocks -destination=mocks/dbfs.go github.com/databricks/databricks-sdk-go/service/dbfs DbfsService ``` * `go run github.com/golang/mock/mockgen@latest` downloads and executes the latest version of `mockgen` command * `-package=mocks` instructs to generate mocks in the `mocks` package * `-destination=mocks/dbfs.go` instructs to create `dbfs.go` file with mock stubs. * `github.com/databricks/databricks-sdk-go/service/dbfs` tells which Databricks package to look services in. * `DbfsService` tells which services to generate mocks for. ## Initializing `gomock` Every test needs the following preamble: ```go ctrl := gomock.NewController(t) defer ctrl.Finish() ``` ## Mocking individual methods with `gomock` Every actual method call must be mocked for the test to pass: ```go mockDbfs := mocks.NewMockDbfsService(ctrl) mockDbfs.EXPECT().Create(gomock.Any(), gomock.Eq(dbfs.Create{ Path: "/a/b/c", Overwrite: true, })).Return(&dbfs.CreateResponse{ Handle: 123, }, nil) ``` ## Testing idioms with Databricks SDK for Go You can stub out the auth with the `databricks.NewMockConfig(nil)` helper function. Every service has a public property with the name of the service plus `Service` suffix. You have to manually set the stubs for every service that is called in unit tests. ```go w := workspaces.New(databricks.NewMockConfig(nil)) w.Dbfs.DbfsService = mockDbfs ``` ## Running this example 1. Run `go mod tidy` in this folder to create `go.sum` file to pick dependency versions. 2. Run `go mod vendor` to download dependencies into `vendor/` directory. 3. Run `go generate ./...` to create `mocks/` directory. 4. Run `go test ./...` to invoke tests with mocks.
This adds support generating mock for interfaces with generics See golang/mock#621
This adds support generating mock for interfaces with generics See golang/mock#621
This adds support generating mock for interfaces with generics See golang/mock#621
This adds support generating mock for interfaces with generics See golang/mock#621
This adds support generating mock for interfaces with generics See golang/mock#621
Even when trying with the |
I got generics working by using the main branch and passing the |
Getting the same issue too. Tried the main branch as well. The issue seems to be happening when I have a constraint declaration: type Number interface {
int64 | float64
} |
Similar to you I am seeing something along the lines of This is when going over a just any reference to my generics such as a method in an interface: Search(input InputInventorySearch) (*internal.Pagination[internal.Product], error) |
hi @codyoss , when is 1.7.0 expected to be released? |
Requested feature
Mockgen should support generating mocks for interfaces with generics.
Why the feature is needed
The Go 1.18 is planned to be released soon, and I noticed that the mockgen tool doesn't support the generic. When a type parameter is in the interface, mockgen will throw an error.
Code example: https://go.dev/play/p/g1G2OvZPQpT?v=gotip
(Optional) Proposed solution
StudentRepository
interface is concrete, so the gererated mock should be concrete too with the type parameters substituted.BaseRepository
interface is abstract, so the generated mock struct should also have the same type parameters.(My use case only needs 1, which mockery supports as of today.)
The text was updated successfully, but these errors were encountered: