While working on my personal project in Golang, I ran into the usual withdrawal symptoms that any pivot experiences when they leave the comfort of the BDD tools afforded to rubyists.
Enter Ginkgo and Gomega, these tools helped take the edge off. I started writing my tests with them and they saved a ton of boilerplate setting up, building failure messages, and making assertions.
This set up got me through three modules until intermodule dependencies started creeping up. I found that the tests were becoming integration tests, they had no trouble telling when something was wrong, but sent me digging through stack traces to find out what. Since that is not the way I like to build software (anymore), it meant it was time to think about dependency injection and *gasp* stubbing/mocking/spying (in a strictly typed language).
I tried a few patterns before settling on one that I liked. For instance, I tried introducing generator interfaces to all my modules but I found myself needing to store generators in my other structs in order to know what dependency to use. It was much too cumbersome so I scrapped it. The pattern I settled on is simple, each module defined the following…
type Interface interface {}
New = func() Interface { return &structThatImplementsInterface{} }
Now if I wanted to swap out a dependency, I could just set the New function. Once I had all my dependencies composed with New functions and interfaces injecting dependencies, it would make mocking/stubbing/spying easy.
I got started writing my first unit tests and immediately noticed that I would need tons of boilerplate every time I wanted to mock something out. Enter Gomock, while it isn’t the simplest solution, gomock gives you a neat way to create mocks out of your interfaces.
Of course, the first trouble you will run into using Gomock with Ginkgo is Gomock’s reliance on the testing.T object passed in by the built in testing framework. Fortunately for me, the problem had been solved. So I got my mocks generated and started writing tests.
There was one more pain I ran into. The biggest convenience of BDD ala Ginkgo is the convenience of nested messages and test setup, so using Gomock, which rigidly checks that all calls were expected and if expectations are met can make that nested setup impossible.
What I needed was a way to allow calls to the mock without expecting them. After digging around through gomock, I decided that it was too much fuss for me to implement allow. However, I didn’t return from my dig empty handed. There were some tricks I learned that could be exploited to get similar behavior. These tricks involved exploiting the gomock.Call. You can expect your function to have been called any number of times…
myMock.EXPECT().MyFunction().AnyTimes()
The problem is that if I try to “Allow” my call in the BeforeEach when I expect the same call in one of my It blocks, it adds a second expectation that doesn’t get fulfilled. The solution is to save a pointer to the call, and change the number of times you expect it. You can do it like so…
var myCall *gomock.Call
BeforeEach(func() {
myCall = myMock.EXPECT().MyFunction().AnyTimes()
})
It("expects to receive myCall once", func() {
myCall.Times(1)
})
Once you know that, its a peice of cake! It would be a good goal to make this use case easier somehow, but until then this solution is just dry and clear enough to be useful.