vektra / mockery

A mock code autogenerator for Go
https://vektra.github.io/mockery/
BSD 3-Clause "New" or "Revised" License
5.79k stars 396 forks source link

Unable to generate interface type from another interface #787

Open FournyP opened 2 weeks ago

FournyP commented 2 weeks ago

Description

Hi, I wanted to write some mock for my interface :

type ResponseLessUseCase[M any] interface {
    Execute(message *M) *errors.Error
}

type SendEmailVerificationUseCaseInterface ResponseLessUseCase[usermessages.SendEmailVerificationMessage]

But only the mock for ResponseLessUseCase was created, maybe I miss something ?

Mockery Version

v2.43.2

Golang Version

v1.22.3

Installation Method

Steps to Reproduce

  1. Install go & mockery last versions
  2. Write a golang file that include a generic interface and another interface that is typed as your generic interface
  3. Run mockery

Expected Behavior

A mock for the interface that inherit the generic one

Actual Behavior

Only the mock of the generic interface

FournyP commented 2 weeks ago

Here is my .mockery.yaml config :

with-expecter: true
all: true
filename: "mock-{{ .InterfaceName | kebabcase }}.go"
outpkg: mock{{.PackageName}}
mockname: "{{.InterfaceName}}"
dir: tests/mocks/{{trimPrefix .InterfaceDirRelative "pkg/"}}
packages:
  packageA:
    config:
      recursive: true
LandonTClipp commented 2 weeks ago

Hmm that's an interesting use case I have not thought of (generic interface with an instantiated type assigned to a different type name). It should work but I'm not sure what mockery thinks of this. Can you run with mockery --log-level debug | grep SendEmailVerificationUseCaseInterface and see what mockery does with it?

FournyP commented 2 weeks ago

Hi @LandonTClipp,

I got logs for ResponseLessUseCase but not at all for SendEmailVerificationUseCaseInterface

LandonTClipp commented 2 days ago

If mockery did not print anything then something has gone wrong during its discovery phase of top-level interface definitions. I'll see if I can tinker with it.

LandonTClipp commented 2 days ago

Well this is hilarious. The Go AST considers this to be an index expression: *ast.IndexExpr. Mockery does not see SendEmailVerificationUseCaseInterface as an interface type because of this. I can successfully induce mockery to generate a mock for it by telling it to include *ast.IndexExpr the list of types it generates mocks for (currently only interfaces and functions), but I'm not entirely sure on the implications of this. I need to think about it.

There's three ways we could resolve this:

  1. Provide an override in config to force mockery to generate the mock for unusual types like this. This is the least risky but also generates more burden on users who define their types in this way.
  2. Always generate mocks for *ast.IndexExpr. This is highly risky as I'm unsure the ways in which this might break.
  3. Submit a PR to the Go developers asking them to make type declarations that use generic instantiation to appear in the AST as interfaces, not index expressions. This is likely the right long-term solution, but it could take quite a while to gain any traction, if at all.

PR: https://github.com/vektra/mockery/pull/790