Gomock Tutorial
I’ve been using mock/Gomock
to write tests in my personal project. When you’re building something in a new
language, it is hard to prioritize learning every tool in your toolchain. For me, I’ve been writing custom and
suboptimal code for Gomock because of a nifty but undocumented API call .Do
.
In many cases, I wan to match subsets of a complex object while ignoring irrelevant parts. e.g., verify a function is
invoked with a list of User
objects, but only verifying the email addresses. To do that in a generic
way, I wrote a custom Matcher API that uses text/template
to describe what parts of the object to match. Thus, my
mock-and-verify code looks like:
ctrl := mock.NewController(t)
defer ctrl.Finish()
mockMessageSender := NewMockMessageSender(ctrl)
matchFrom := ...
// This matcher returns true if the argument is a list whose objects have Email of "recipient1@..." and "recipient2@..."
matchTo := utils.M("{{(index . 0).Email}} {{(index . 1).Email}}", "recipient1@example.com recipient2@example.com")
matchOutMessage := ...
mockMessageSender.EXPECT().Send(matchFrom, matchTo, matchOutMessage).Return(nil).Times(1)
My custom Matcher was only twenty-ish lines of code. But it never felt clean to me. I was looking for something more
like Mockito
.
ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
verify(mock).doSomething(argument.capture());
assertEquals("John", argument.getValue().getName());
Or
// more complex Java 8 example - where you can specify complex verification behaviour functionally
verify(target, times(1)).receiveComplexObject(argThat(obj -> obj.getSubObject().get(0).equals("expected")));
It turns out there was a similar feature in Gomock that isn’t documented, the Do function. You can use it this way:
import (
Assert "github.com/stretchr/testify/assert"
)
func TestSend_ok(t *testing.T) {
assert := Assert.New(t)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
...
mockSender.EXPECT().Send(gomock.Any()).Do(func(messageGot email.EmailMessage) {
assert.Equal("foo@user.com", from.Profile.PreferredEmail)
assert.Equal("TODO Add Name to models.UserDDB", messageGot.From.Name)
assert.Equal("Subject", messageGot.Subject)
assert.Equal("Body", messageGot.HtmlContent)
})
...
}
Now I can happily go back and delete my own code!
If you are looking to write good tests for Go, I hope this has been helpful.