derision-test / go-mockgen

MIT License
57 stars 7 forks source link

Generated sub-package contains circular import #43

Open treyburn opened 1 year ago

treyburn commented 1 year ago

Perhaps I am missing a command line arg - but I do not see anything in the documentation that addresses this.

Using go-mockgen@v1.3.7 I see the following error related to generating a sub-package and circular imports to the parent package.

Using the following code and go:generate directive:

package foo

//go:generate go-mockgen -f github.com/user/mod/pkg/foo -i Fooer -o ./mock/mock_fooer.go
type Fooer interface {
    Foo()
}

Generates the following code:

package mock

import foo "github.com/user/mod/pkg/foo"

// NewMockFooerFrom creates a new mock of the MockFooer interface. All
// methods delegate to the given implementation, unless overwritten.
func NewMockFooerFrom(i foo.Fooer) *MockFooer {
    return &MockFooer{
        FooFunc: &FooerFooFunc{
            defaultHook: i.Foo,
        },
    }
}

This causes a circular import error where the intended purpose is to import from the mock package in the parent foo package.

I would expect that the generated code would create a surrogate interface when the generate code is placed in a sub-package. Along the lines of:

package mock

// surrogateMockFooer is a copy of the Fooer interface (from the package
// github.com/user/mod/pkg/foo). It is redefined here to avoid
// circular imports in the parent package.
type surrogateMockFooer interface {
    Foo()
}

// NewMockFooerFrom creates a new mock of the MockFooer interface. All
// methods delegate to the given implementation, unless overwritten.
func NewMockFooerFrom(i surrogateMockFooer) *MockFooer {
    return &MockFooer{
        FooFunc: &FooerFooFunc{
            defaultHook: i.Foo,
        },
    }
}
efritz commented 1 year ago

Could you explain why the defining packages needs to access its own mocks? Are you using go-mockgen to generate non-testing code?

I think this can be supported, but I haven't experienced this need with my own use of go-mockgen yet. For testing purposes, I (personally) try to define interfaces and their mocks at the place they are used, not thee place they are defined. I have colleagues that do the opposite and define the mock next to the defining interface so that it can be shared by consumers. In neither case has the defining package needed to access its own mocks, though.

treyburn commented 1 year ago

Sure in my particular use case I have a builder pattern which takes in a configuration object and returns one of several implementations.

I also provide some helper functions within the same package that encapsulate a number of common interactions using the interface. It is those functions which require the mocks.

And to complicate matters more, I am using these mocks in both whitebox tests in the foo package, and blackbox tests in the foo_test package - so I'm placing it in pkg/foo/mock to be used by both.

Admittedly it's a bit of a cumbersome setup that I'm not totally happy with and I'm still trying to figure out how to improve it.