Discover how to run successful Golang testing and mocking with Gomock in table-driven tests. Learn more in the Gomock tutorial from GogoApps.
In this tutorial, we will present one the key way our Golang development team tackles the problem of writing precise, robust and explicit table-driven unit tests with the gomock package.
If you’re not familiar with the terms used above, we would recommend referring to the following texts first:
Continue reading this Gomock tutorial to discover the best practices when it comes to running a successful table-driven test with Gomock.
The best way of learning is by getting your hands dirty. To kick things off, let's set up a project to test. To show our approach with the gomock package, we’ll create a very simple "party" app.
The main service in our app will fetch the names of people coming to the party and print out greeting messages for everyone to the console.
Note: we’ve placed all the source code on github so that you can play around with it. Throughout this article all the code you'll need is pasted here at each step.
Firstly, you'll need to define a visitor:
...and domain-level behaviors:
For this tutorial the implementation of the interfaces isn’t necessary. Throughout this Gomock tutorial won’t be using concrete implementation as we try to avoid running the dependencies in unit testing.
Finally, to complete the set up, let's create the service with the method we would test:
You might have noticed that the GreetVisitors method is quite tricky to test. This is because:
it relies on its dependencies.
it prints its result and returns just an error (we cannot just check the return).
it has multiple points of return.
Since we’re unit testing, we’re supposed to exclude dependencies from this equation.
There are many ways to mimic dependency's behavior in Golang. The most basic one being just explicitly writing the returns you need.
You can also save yourself some work and use a package for it. We chose gomock because it allows us to precisely set the functions' invocations and is actively maintained by a proactive community.
Mockgen is a go tool which generates structures that implement given input interfaces. It is installed the same way as other Golang tools. Provided you have everything set up all you need is to:
if your project happens to be in GOPATH.
Mockgen has two modes. The definitions have been taken from the package repo:
reflect mode 'generates mock interfaces by building a program that uses reflection to understand interfaces.'
Source mode 'generates mock interfaces from a source file. It is enabled by using the -source flag.' source.
From our point of view the main differences are:
the source mode lets you generate unexported interfaces as it statically parses the code.
reflect mode can be used with go:generate annotations. To use these successfully we recommend referring to a great post by Sergey Grebenshchikov.
reflect mode gives you more control over what, where and how it is generated.
Both approaches are correct and will produce the right results in most cases.
For this tutorial we chose to use source mode. Using this approach, you sacrifice precision for a nice and defined structure, and also the possibility of mocking unexported interfaces.
Here is a Makefile which proves to be useful for that:
Using this Makefile rule, you'll get a mock folder which content directly reflects the structure of the project files:
As you need to add a new file's content to mocks, add it to mocks prerequisites in Makefile and then
Now, let's get to testing. First, we'll start with a simple, single-case test (non-tabular approach):
Please refer to gomock documentation for more advanced expectation settings. Now, when the deferred
ctrl.Finish() is called and the expected calls are not made, the tests will fail.
Now let's try to adapt gomock to use in table-driven tests.
We'll start with tabular test template generated by gotests tool:
As you’ll most likely find, a few problems emerge.
The gomock's design doesn't allow you to both initialize and set up the expected function invocations in a single expression. For this reason, you need to do it "before" test cases.
Also, we want our tests to be idempotent, so each one would need different initialization. As you can imagine, with more than a few test cases it could get quite messy.
Luckily, we’ve managed to find a solution to this. Let's change the structure to the following. All changes are prepended with comments.
Now our tests are fully idempotent thanks to gomock's controller being initialized inside
t.Run and expectations being set for each individual case in prepare function.
Thanks to this feature, you are perfectly equipped to plan exactly what calls are made on your dependencies in each case. If they aren't - you'll get a message indicating which calls are not made.
At this stage it’s time to put everything together and fill in some test cases to test how it works!
While the function doesn't have enough coverage yet, this example is enough to demonstrate the idea.
Throughout this tutorial we have presented the key ways to tackle the problem of writing precise, robust and explicit table-driven unit tests with the gomock package.
Now that you have a valuable approach you can define test cases in a concise way without any extra work.
Constructive critique is welcomed in the comment section below.
Please share if the post helped you!