如何避免模拟生成中的导入周期?

Simple example.

I have package xxx. This package contains:

  • struct A
  • interface B which is a field of A
  • struct C which is an argument in method of B

    type A struct {
        SomeField B
    }
    
    type B interface {
        SomeMethod(c C)
    }
    

Now imagine I want to create unit test for structure A and mock dependency B. For creating mock I am using mock generator. All mocks are stored in the common "mocks" folder.

The problem is that generated mock has a dependency on xxx package. This is happening because SomeMethod of interface B has argument xxx.C.

Whenever I try to import my mock structure in a_test.go it fails because of cycle import problem. xxx package importing mocks package in the a_test.go. and mocks package imports xxx package in my generated mock.

I need a peace of advice, what is the best workaround for this? Maybe my approach is not idiomatic enough. Where do you store your mocks?

Use a top level package that all other packages import from. Put your interfaces there.

For instance:

domain/
    interfaces.go
a/
    mock.go
b/
    mock.go
c/
    mock.go

a, b and c should import from domain so they don't have any dependencies on each other. You will use duck typing to implement the interfaces of the domain package in your mocks.

Here's an actual use case using your example:

domain/interfaces.go:

type A interface {
    Foo()
}

type B interface {
    Bar() string
}

type C interface {
    Baz() string
}

a/mock.go:

type A struct {
    SomeField domain.B
}

// ...

b/mock.go:

type B struct {
    SomeMethod(c domain.C)
}

// ...

c/mock.go:

type C struct {}

// ...

That should compile just fine, because all the mocks import from the top level domain package, and they all implement the respective interfaces.

You need to put your test under a different package.

a.go is under package xxx

a_test.go is under package xxx_test

a_mock.go is under package xxx_mock

This way a_test.go will be dependent on xxx and xxx_mock and will not cause dependency cycle.

Also, a.go and a_test.go can be under the same folder, like this:

xxx/
  - a.go
  - a_test.go
mock/
  - a_mock.go