a laptop on a table
Photo by Arnold Francisca on Unsplash

Generate Mocks In Go Like A Boss

Generating mocks automatically avoiding cyclic dependencies and keeping the package directory as clean as possible

Matteo Pampana
5 min readFeb 4, 2023

--

Have you ever generated a mock with some automatic tool and miserably ended up with a cyclic dependency issue?

Have you managed to overcome this issue by generating many files in your package directory, messing up the last piece of readability?

I've been there, done that. I'm not in this situation anymore, and I want to share with you how I escaped the mud.

Let me write my own mocks!

The first, the most direct way of generating mocks is … writing the mocks yourself!

With this amazing “technique” you will not have cyclic dependencies issues, you will not bloat your package directory, and you will have many pros. But you may waste a lot of precious time.

You must define a new structure and implement its methods for every interface you use. If you want any fancy features like spying, you must implement them.

You can easily end up spending more time designing your mocks than your business logic.

Remember: business logic is the only thing that brings business value!

Generate my mocks, please.

You want to generate your mock automatically, don’t you?

When I have to choose between mocking frameworks in Go, these two are the ones that always come into my mind:

Here, I do not want to dig deep into any of these frameworks, but I must say that I prefer to use mockgen since it allows generating multiple mocks in a single file.

Message for the mockery fans: mockery is great, but — at the time of writing — it generates a file for each interface to be mocked. This will bloat your package directory.

I know, you may object to my last sentence suggesting I use a different package for the mocks. Sure, I could.

The hard truth is that you cannot always exploit this option for generating your mocks.

Here you can see an example:

package foo

type MockMe interface {
Method(p Request) (Response, error)
}

type Request struct {
x int
}

type Response struct {
ok bool
}

If you generate the mock for the MockMe interface, it will include a dependency for the Request and Response structures.

Putting the mock in a different package will depend on the package you want to test (foo).

Finally, when you use this mock inside your test file, you will have a beautiful cyclic dependency.

In this situation, to avoid cyclic dependencies, you need to keep the mocks inside the package you have to test.

If you choose to use mockery, it will generate one file for each interface to be mocked.

When the number of interfaces grows, the framework will fill your package folder with a lot of files. I do not like this solution 👎

If you are like me, you would prefer to keep the package directory clean: you want one file with all the mocks inside.

This choice, in my point of view, can increase the readability of your codebase.

If you are not working alone, your colleagues will be happy to see clearly which files have the uttermost value in a directory.

What I looked for

I was looking for a mocking solution with the following properties:

  • It is effortless to generate mocks;
  • It does not fall into the cyclic dependency issue;
  • It does not mess up the package directory with many files: ideally, I want just one file for all the mocks;
  • It does not affect code coverage;
  • Anyone that needs to modify that piece of code can easily and effortlessly regenerate the mocks.

After a period of struggling, I came up with a solution that satisfies me.

My solution

package foo

//go:generate mockgen -package foo -source=models.go -destination=mocks_test.go *

type MockMe interface {
Method(p Request) (Response, error)
}

type MockMe1 interface {
Method1(p Request) (Response, error)
}

type MockMe2 interface {
Method2(p Request) (Response, error)
}

type MockMe3 interface {
Method3(p Request) (Response, error)
}

type Request struct {}

type Response struct {}

1️⃣ The first thing you need to do is to put all of your interfaces in a single file. I usually name this file models.go, and I use it to keep all the structures I need across the package.

2️⃣ Then, I write a go:generate command. Many IDEs and Code Editors (such as VSCode) can execute the command after the go:generate clause just by clicking on it. Cool!

3️⃣ Finally, I call the mocking framework to generate the mock files on my behalf. The -package option tells mockgen to use a specific package (the same that is being tested), the -source option tells which file contains the interface declarations, and the -destination option tells the name of the file you want to generate. The * at the end tells mockgen to generate the mocks for all the interfaces in the source file. (you can explore more options in the mockgen documentation)

4️⃣ You may have noticed I put the _test suffix in the output file: by doing this you are telling Go not to take into account this file while computing the code coverage. Since this file will be used for test purposes, this suffix is more than acceptable.

Alternative Solution

You can put the test in the foo_test package to avoid cyclic dependencies: since the test is not in the same package as the unit that is being tested, you break the cycle.

This is a smooth solution, but there is a situation in which I could not apply it. Consider having to test the internals of something that depends on time.Now function. The most common solution is to put the test inside the same package and re-assign to some fake now function that returns a constant time.Time value. This makes the test replicable.

To clarify the scenario, I want to give you an example. In your business logic you have something like this:

package foo

type Service struct {
dependency1 MockMe
dependency2 MockMe2
dependency3 MockMe3
}

func (s *Service) MyLogic (r Request) (Response, error) {
now := time.Now()
// some logic using now
}

If you want to make the test deterministic, and you want it, you have to redefine the now function in your test.
You could rewrite your service like this:

package foo

type Service struct {
dependency1 MockMe
dependency2 MockMe2
dependency3 MockMe3
now func() time.Time
}

func (s *Service) MyLogic (r Request) (Response, error) {
now := s.now()
// some logic using now
}

Conclusions

Generating mocks in Go is generally easy. You can create them on your own if you want.

Usually, in a professional environment, it is better to generate them automatically using a mocking framework. Then, you must be aware of some details that can make you shout in front of your laptops.

You want to avoid cyclic dependencies and if you want to keep the package directory as clean as possible, you have to use the right tool and the right strategy to generate every mock in a single file.

In this article, I shared my strategy for doing so.

Join the Discussion!

If you have read the story until here, thanks! It means a lot to me. ❤️

I’d love to hear from you. Your experiences, insights, and opinions are invaluable.

👇 Drop your comments below, clap if you found this article helpful, and don’t forget to follow me for more insights.

Feel free to connect with me on LinkedIn for further discussions ✍️

Let’s keep the conversation going!

--

--

Matteo Pampana

Senior Software Engineer @ Adevinta 💻 Italian guy 🇮🇹 sharing with you what I am learning during my journey as a software engineer.