Mocking an Interface using Mockery in Go

Mert Kimyonşen
Delivery Hero Tech Hub

--

Mocking interface is one of the approaches for unit test. If you developed your program based on interface/abstraction, you can make your test easy.

Go’s standart library provides the built-in testing package and go test commands. This library help us to make test easy but sometimes we need more complicated test that required mocking.

If you are not familiar about unit test, I recommend you to read my post about How to Write Unit Test in Go before continue.

Let’s start with Go’s Interface. Because Go’s interface is different.

About Go’s Interface

Go is not an object oriented language. Go’s interface is different from other programming language. It’s only an abstraction type in Go. It is a custom type and it’s defined a set of method signatures.

Here is simple interfaces in Go.

It is similar to other programming language syntax such as C#, Java, Typescript etc..

Mocking an Interface

Either you can create mocking an interface on your own way like without using any third-party tool or create mocking an interface by using third-party tool.

First approach is fine but it will be increase boilerplate codes in your program like following code.

Second approach is a fancy. There are lots of benefits like following.

  • reducing boilerplate codes
  • collecting mocking interface in a common
  • very fast creating mocking interface
  • have many options to mock interface
  • quick integration with CI

There is a great package for second approach, it called mockery I got experience on it and I love to use it.

mockery

The mockery provides generate mocks for interface and it removes the boilerplate codes required to use mock.

You can install mockery through brew, run the following code.

$ brew install mockery

To ensure mockery installed on your system, run the following code.

$ mockery --version

You will revive the result like following.

2.9.4

There is many flag option in mockery tool. If you want to see them, run the following command and see the mockery options.

$ mockery help

How to use this tool? Don’t worry, we will demonstrate a demo with a small system to better understand test and mock.

We have a product domain. The flow is simple and its purpose is saving product data into database.

ProductRepositoryInterface like a third party service and it has Add(..) method and returning error.

We are going to mock this interface for our test purpose after.

ProductRepository implements requirement of the ProductRepositoryInterface.

The ProductService responsible for inserting data by using ProductRepositoryInterface and initialize with the ProductRepositoryInterface so the service depends on the repository.

Project folder looks like following.

.
├── go.mod
├── go.sum
├── internal
│ ├── models
│ │ ├── insert_product_dto.go
│ │ └── product.go
│ ├── repositories
│ │ ├── product_repository.go
│ │ └── prouct_repository_interface.go
│ └── services
│ ├── product_service.go
└── main.go

Now, we are going test the ProductService. Before the test, we need to generate mocking the ProductRepositoryInterface. Because the ProductService needs the ProductRepositoryInterface to initialize.

Following the command will generate mocking an interface. Remember that, if you want use mockery tool, you have to install it as we mentioned above.

$ mockery --dir=internal/repositories 
--name=ProductRepositoryInterface
--filename=product_repository_interface.go
--output=internal/mocks/repomocks
--outpkg=repomocks

Let’s explain the mockery some common flags.

  • — dir flag is directory of interfaces
  • — name flag is to generate mock for
  • — fileName flag is name of generated file
  • — output flag is directory to output mocks
  • — outpkg flag is generated file package name

After run the command mockery will generate a file like the following.

The file can’t editable. If your repository is expanding by adding new methods, you should generate each time to get updated mock of your interface.

As we mentioned above, you can find more options typing mockery help on your terminal.

Now, we are ready to test the ProductService. The test purpose is inserting product data with ProductService without any error. Sometimes calling happy path test

As we know that, The ProductService needs to ProductRepositoryInterface. The ProductRepositoryInterface saving product data into database with it’s Add(..) method. We need to make mock of ProductRepositoryInterface work just like actual interface.

Let’s explain the test codes.

In line 14, we initiated mock of ProductRepositoryInterface that we created by mockery.

We’ve arranged mock of ProductRepositoryInterface to fit our test purpose. We’ve specified Add(..) method will work with product model. Then we set return value nil and indicate that it will only run once.

In line 15, we took advantage of mock functionality. We set Add(..) method to run when method is called.

  1. On(..) function takes two inputs. They are method name and arguments. The method name that we want to invoke (Add(..)). Arguments is method arguments (models.Product) that we want to pass it for test purpose.
  2. Return(..) is return value of the method.
  3. Once() is only run once

The final project folder looks like following.

.
├── README.md
├── go.mod
├── go.sum
├── internal
│ ├── mocks
│ │ └── repomocks
│ │ └── product_repository_interface.go
│ ├── models
│ │ ├── insert_product_dto.go
│ │ └── product.go
│ ├── repositories
│ │ ├── product_repository.go
│ │ └── product_repository_interface.go
│ └── services
│ ├── product_service.go
│ └── product_service_test.go
└── main.go

Now we are ready to test our codes.

Following command will test the ProductService.

$ go test -v internal/services/product_service_test.go

The result:

Conclusion

Well, we’ve demonstrated a small demo to mocking an interface by mockery tool. The idea is how to take advantage of mocking interfaces by using third-party tool for test purpose.

That’s all for now. Thank you for reading.

Source Code

--

--