Mocking in Golang

Deniz GÜRSOY
6 min readJan 7, 2023

--

Mocking is an essential concept in testing. Every language has different libraries generating mocks for tests. In this post, we will cover how we can create mocks in Golang.

How it is done in Golang

Mocking can be done with the gomock library developed by Google. I had some experience with the gomock library. But I could have been a better fan of creating a controller and deferring its finish method. I looked for if there is any alternative and I learned about the library mockery. Finally, I decided to stick with the gomock library because I find it easy to use in comparison with mockery even though there are also great features of mockery that I liked. At the end of this post, I will make a comparison of these two libraries.

https://www.kindpng.com/imgv/hbJTbmm_addthis-sharing-buttons-transparent-png-golang-gopher-png/

Why do we need mocks? A case study

Let’s assume we work for a logistics company. The delivery logic in the company is to send mail to the work addresses of the customers first and if there is no work address registered in the system, you need to mail them to the home addresses of the customers.

I want to make sure that my code calls the address service and returns the correct postcode based on the logic. Take the following data as an example.

| CUSTOMER_ID | WORK_ADDRESS_POST_CODE| HOME_ADDRESS_POST_CODE|
| 1 | 2544TT | |
| 2 | | 1111TK |

There are two cases that I need to test:

  • GetPostCode(1) should return 2544TT
  • GetPostCode(2) should return 1111TK

In my tests, I need to create a new PostService, but it takes the address service struct as an argument. If I initialize a new address service, and when I run the test, it will try to execute the business logic of the address service. Hence, it might need to connect to DB to fetch real data.

At the moment, I need something which returns the data I need in my test and which can replace with address service type. As long as NewPostService function expects address service struct type, I can not achieve that.

However, If it takes an interface type as the argument, I would be able to call the NewPostService method with any implementation of the interface ( a mock implementation or the real implementation.)

With the help of an interface, I can create a mock type that implements the same interface and send the mock type in my tests instead.

This kind of implementation is actually a design pattern called strategy pattern. I find the strategy pattern very useful for writing clean code. Strategy pattern helps you do loose coupling by abstraction.

Let’s define an interface that has the same method and signature.

Now, the address service implements the Locator interface. I need to make a small change in the NewPostService method so that it takes Locator as an argument.

How not to mock

I can define a new type mockAddressService which also implements the Locator interface. But this interface executes my test logic.

Since mockAddressService is a Locator, I can pass it as an argument to the NewPostService method. ( That is why mocking is related to strategy pattern because the mock type and the actual type implement the same interface but they execute different logic. )

What happens if you have many interfaces and many methods that you need to mock? You need to write a lot of code to just test your code. What happens if the original interface changes? Then you need to spare time for maintaining the mocks as well. If you have a lot of test cases, then your mock has to return different data for different test cases. Since you may not be able to manage all cases in one mock, you may end up creating new mock types for different tests.

Wouldn't it be great if there were a generic solution, where I can create a new mock in every test and configure the mock to return particular data in particular cases? Thanks to gomock, you can do that easily.

Generating mocks of interfaces

Gomock uses a binary called mockgen to create mocks from interfaces. This binary generates codes you need for mocking. Mockgen can be installed with:

go install github.com/golang/mock/mockgen@v1.6.0

Then you can create mocks of interfaces in a source file with the following command:

mockgen -source=address.go -destination=mock_address.go 

This command will generate mock source code of all interfaces to the destination file mock_service.go and you will have now NewMock function for every interface you have in the source file. In this case, you have the NewMockLocator function to create a mock.

These mock-generation processes can be combined with //go:generate directive as follows:

//go:generate mockgen -source=address.go -destination=mock_address.go

You combine the command with go generate and add it to first line of your source file. You can generate all your mocks in your project by simply running:

go generate ./…

A Mock can be created as follows:

If you inspect the mock, you can see that it has all the methods of the interface. In tests, your code will call the methods on the mock. The question is how I can make the mock return values I want to use in the test.

Mocks also have EXPECT() method allowing you to create calls and configure them. Now I want to create a call to the GetAddress method on the Locator interface to return &Address{“2544TT”} when it is called with the arguments customerID=1 and addressType= WorkAddress. This can be achieved simply by calling EXPECT() methods like:

Now the test looks like this:

To test the second case, I need to make sure that the first call for the work address should return nil and the second call for the home address should return &Address{“1111TK”}.

Matching arguments

In the cases above, I used fixed values in the calls. Sometimes you might want to make sure that the function you test calls other interfaces with correct arguments. To do that, Gomock has some utility functions that you can use in arguments. If the arguments do not match, gomock fails the current test.

Writing mock implementation

Instead of returning fixed values to calls, you can also execute custom code for a specific call. See the following as an example:

Ordering the calls

Normally, calls are not executed in any order. If you want to arrange calls in order, you can use the After method of the calls or gomock.InOrder method to arrange calls sequentially.

Repeating the calls

If you need to duplicate a call, you do not have to create another call. A call has some methods allowing you to repeat calls.

Caveats

While using gomock, you should pay attention to the fact that every call you create must be executed in your test just like you stated in the call. For example, if you replicate a call with Times(2) and it is called only once, your test will fail. If the arguments do not match, your test will fail.

One downside of gomock is that the Return function of a call is a variadic function. It means it does not know how many arguments the actual method returns. In case of a change in the return types, the compiler will not give any error. You can only see the errors if you execute the tests again.

Remember to regenerate mocks after you change the interfaces.

Keep in mind that if you do not prefer interfaces over structs, mocking and naturally unit testing are compelling tasks. If you are dependent on another service, and you want to test whether that service is called correctly or not, then you should define interfaces for the concrete types like structs and use that interface for the target service.

Gomock vs Mockery

References

--

--