golang / mock

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

Mockgen can't handle anonymous struct type #153

Open ChuntaoLu opened 6 years ago

ChuntaoLu commented 6 years ago

Mockgen can't generate mocks for interfaces whose method param or return types are unnamed non-empty structs. For example:

Suppose I have a foo.go file whose package import path is foo:

package foo

type Client interface {
    Get() struct{ x int }
}

Running source mode mockgen -source=foo/foo.go gives

 failed parsing returns: foo/foo.go:4:8: can't handle non-empty unnamed struct types

Running reflect mode mockgen foo Client gives

Reflection: can't yet turn struct { x int } (struct) into a model.Type
codyoss commented 4 years ago

@ChuntaoLu is this a common pattern you use? I had never seen an anonymous struct returned until just now 😸

alvinmatias69 commented 3 years ago

@codyoss for me it's commonly found in legacy apps. Where the function arguments or return type is an anonymous struct

bjohnson-va commented 1 year ago

I'm actually warming up to this pattern when the opportunity arises.

Consider this example, where I have some low level implementation of a thing:

package lowlevel 

type LowLevelThing struct {
}

type LowLevelData struct {
  ID string
  Value int
}

func (l LowLevelThing) GetData() LowLevelData {
  ... the implementation ...
}

And now I have some high level module that wants to use the behaviour of the LowLevelThing. But, following the Dependency Inversion principle we're going to define our own interface so we're not coupled to the implementation.

package highlevel

// Look at me, I'm doing dependency inversion so I don't need to know about the implementation!
type Dependency interface {
  GetData() lowlevel.LowLevelData // <--- Shoot! Now I know about the implementation!
}

type HighLevelThing struct {
   dependency Dependency 
}

To avoid this problem, LowLevelData could just as easily return an anonymous struct with primitives:

func (l LowLevelThing) GetData() struct {
  ID string
  Value int
} {
  ... the implementation ...
}

And now the high level module doesn't need to know about the implementation.

//
// Look at me, I'm doing dependency inversion so I don't need to know about the implementation!
type Dependency interface {
  GetData() struct { // <-- And I mean it this time!
    ID string
    Value int
  }
} 

A struct is preferable over multiple return values, especially when there are multiple of the same type:

type Dependency {
  GetData() (string, string, string, string) <-- 🤔 which one was foo, again? Guess I'll go look at the implementation.
}
bjohnson-va commented 1 year ago

As a workaround, you can temporarily add a named struct to your implementation and update it to return that named struct before running code gen. Then undo those changes and manually update the mock to return anonymous structs.

Obviously not ideal, but a workaround nonetheless if you don't re-gen often.