Writing a custom matcher for testing with gomock
Everyday we write tests for our applications. While writing a test, we usually mock integrated parts. Doing so, we aim to hide the implementation details of the integrated parts. Thanks to mocking, we do not need to modify our tests whenever there is a change in the implementation of mocked parts.
gomock
is a package for mocking in go. It matches given expected values with actual ones. Sometimes, we need to customize the way it works. In this post, we will investigate such a case.
Let’s say, we have /product
endpoint for inserting new products into our repository. We use a POST
method handler for this.
Our handler receives requests and interacts with the service to add a new product into the repository. Below is a service test for the happy path scenario:
If you look at our test, you can see that we mock the repository and expect to call the repository’s Insert
function with expectedProduct
. When we run the test we face an issue:
Got: {Product - ID:000001 Description:some-description Quantity:5 CreatedAt:2022-01-02 19:58:24.594343 +0300 +03 m=+0.001580715} (*product.Product)Want: is equal to {Product - ID:000001 Description:some-description Quantity:5 CreatedAt:2022-01-02 19:58:24.594045 +0300 +03 m=+0.001282804} (*product.Product)
The issue is that the expected and actual values given to Insert
function are different. Why? 🤔 After we make a deep dive, we see that the problematic part is createdAt
field. There has been a subtle difference due to time.Now()
.
Under the hood, gomock
uses some predefined matchers to work. In its official doc Matcher
is defined like:
A Matcher is a representation of a class of values. It is used to represent the valid or expected arguments to a mocked method.
You can check them further here. By default, gomock
matches expected values with actual ones using eqMatcher
. This is where our problem on createdAt
field starts.
In order to overcome this issue, we need to implement a custom matching mechanism for our Product
model. gomock
provides an interface for this:
If you look above, you can see that we can achieve this by implementing Matches
and String
methods. Here is our implementation:
In Matches
function, we check if all fields are equal except for CreatedAt
. Also, we implement String
method to describe the fields that the matcher needs for matching.
Voila ! Now, the test is passing 🥳
To sum up, we covered how we could write a custom matcher with gomock
to use a different matching strategy. That was all for this post, see u in the next episodes 🧐 🚗
References: