After a week or two of hacking with Go for just a few short hours, I can unequivocally say I love it. I resisted learning Go because I thought static typing would make me less productive. I love(d) monkey patching with Ruby! There is (was) so much freedom that to get something running quickly all I needed was to see “OK” after running

1
ruby -c
. Little did I know the mindspace occupied by that nagging worry in the back of my head saying “remember to write unit tests!” And, “I can almost certainly be sure that all my variables are spelled correctly.” Turns out there was a huge cost to be paid for this freedom, because my code was often buggy, and I never got around to 100% test coverage. Static type checking removes that issue for me and now that I have aligned my brain around struggling a little bit more during compilation, my code just works once I am past that phase. With Ruby all the problems were in the runtime phase, my problems are rarely there with Go.

Testing is well integrated into the go build process, but because there is no monkey patching, testing takes a little more work. The first project I decided to do with go was docker-search, a tool to search docker images better than just keywords in the docker index, and I needed to mock out the http client calls I was making to the docker index and registry servers (to retrieve the full Dockerfiles). There are a lot of examples out there for mocking out server side http calls, but not so many about client side calls. Fortunately, it turned out to be trivial. I refactored my

1
client.go
library so that I could pass in an interface with a single
1
Get()
method. Then, inside the initialization of the client within the actual tool I pass in a real http client, and inside my tests I pass in a fake client.

Interfaces in Go were slightly different than I expected, because I initially assumed an interface would need the same exact signature down to the receiver “class” but in fact Go does not have classes. This means that if you define a method like

1
2
func (a* A)
Get( url string ) string
that it can be used in the same place as
1
func (b* B ) Get( url string ) string
. So, to implement my interface, I just define a two structs, one inside the tool which then defines the method with a real http client, and one inside the tests with a mocked out client.

Code speaks louder than words. I built a

1
Client
which had a
1
WebClient
interface inside it. This interface supports a single method
1
Get()
. My test code just mocks this out and returns different test data based on the request URL, and the real client uses the real
1
http.Client
from the
1
net/http
Go library.

I am still unsure about how to write test code when using channels and goroutines. I used goroutines to execute HTTP requests in parallel and used channels to communicate success back once the asynchronous request had completed. I had to do a little refactoring to make sure the

1
Get()
method was implemented in a way where I was only doing the HTTP request. Initially my
1
Get()
method (after the first refactor) still did both the HTTP GET request and signalled back on the channel which crashed the test code, but beyond this, my code did not need to change much at all.

Check out the full code and tests on GitHub