Go, being a relatively new language doesn’t have a standard or best practice for mocking and testing.  We’re big into CI and unit testing and in order to do it right you need the right testing infrastructure in place.   Although golang’s standard library testing infrastructure

requirements:

  • partial mocking
  • 3rd party & standard library mocking library
  • mock gen
  • a simple test framework that allows for test suites (i.e. tearDown/setUp functions)
  • code coverage reporting
  • easily find which lines of code are not covered.

Partial Mocking

Partial mocking allows us to not just mock out the entire package and test some given functionality.  This isn’t solved by “withmock” because it mocks the entire package, not just a given method name.  Here we can use a simple method that overrides the real implementation with a mock method but then returns it to it’s original function when the test is complete.  Fortunately, Go treats functions as first class citizens and we can assign functions to vars.  For instance:

var myNameMethod = func(name string) string {
   //do something name
   return name
}
newName := myNameMethod()

Now we can swap that function out and replace it with our mocked function for testing.

defer util.Patch(&myNameMethod, func(path string) {
    //assert values
}).Restore()

The next time we’ll call myNameMethod it’ll call my mocked function.  When we’re done testing it’ll restore back to it’s original function. This method was derived from the golang-nuts group on google.  You can see the Path/Restore implementation here: https://gist.github.com/imosquera/6716490

Mock Generation

One of the most painful parts about testing is creating mock interfaces.  Even after creating the mock there is still a lot of boilerplate that needs to happen to make sure these methods get called with the correct parameters.  This is where mockgen from the gomock coding package shines in: https://code.google.com/p/gomock.  First the mockgen package lets you create mock code from a set of interfaces on a given package.  To generate source code:

mockget -source=./mypkg/pkg.go

This generates the mock but you’ll have to place the generated mock in the correct package.  This tool also adds additional functionality to assert that methods were called with the correct parameter and in the correct order.

gomock.InOrder(
    pkgObj.EXPECT().SomeMethod(1, "first"),
    pkgObj.EXPECT().SomeMethod(2, "second"),
    pkgObj.EXPECT().SomeMethod(3, "third"),
)

You can read about the package details: https://code.google.com/p/gomock/

3rd party & standard mocking library

In order to test efficiently we’ll need to be able to quickly mock out libraries that we use directly.  The suggested mocking examples lean passing interfaces instead of structs. but what happens when the 3rd party library you’re using doesn’t use interfaces and passes concrete structs back to your code?  One option is to wrap their code in interfaces but that could be time consuming and create more problems.  Another option is to use the “withmock” library: https://github.com/qur/withmock.  In order to mock a library you’ll add this comment “//mock”  to the end of an import:

import (
	"fmt"
	"os"
	"os/exec"
	"example.com/some/external/package" // mock
)

What this will do is tell “withmock” to generate mocks for:

"example.com/some/external/package"

The “withmock” framework generates the mocks but relies on the gomock framework (https://code.google.com/p/gomock/) to initialize the mock in code.  You can now configure your mocked external package with:

// Setup the ext mock package
ext.MOCK().SetController(ctrl)

This initializes the mock and allows you set method expectation and assert they were called:

// We expect to see HandyMethod called, and we want to return true
ext.EXPECT().HandyMethod().Return(true)

You can read more about each package at: https://code.google.com/p/gomock/ and https://github.com/qur/withmock

 A Testing Framework

Since we have mocks and potentially fixtures that need to be loaded for tests it’d be nice to have some way of tearing down and setting up the fixtures for each test. Additionally, Go doesn’t come with assertions.  This is because developers use them as crutches in real code but they serve a good purpose in test code.  The Gocheck library provides exactly this type of functionality.   There are other frameworks available but they require you to use their own binary instead of the standard “go test”.  This is problemmatic because all of our other tools rely on using “go test” as a foundation.  Using the Gocheck framework gives you access to the following calls

  • func (s *SuiteType) SetUpSuite(c *C) - Run once when the suite starts running.
  • func (s *SuiteType) SetUpTest(c *C) - Run before each test or benchmark starts running.
  • func (s *SuiteType) TearDownTest(c *C) - Run after each test or benchmark runs.
  • func (s *SuiteType) TearDownSuite(c *C) - Run once after all tests or benchmarks have finished

In addition the Gocheck library gives you assertions which are great for testing:

  c.Assert(value, Equals, 42)
  c.Assert(s, Matches, "hel.*there")
  c.Assert(err, IsNil)
  c.Assert(foo, Equals, bar, Commentf("#CPUs == %d", runtime.NumCPU()) }

You can learn more about Gocheck here: http://labix.org/gocheck

Code Coverage Reporting

This is a critical metric that we measure when we develop new code at Socialize.  This coverage reports let’s us know how far from our goal we actually are.  We strongly believe in “What you don’t watch will fail”.  And although code coverage doesn’t always represent how well the code is tested, by not measuring this at all means we would probably let overall coverage slip.  Gocov (https://github.com/axw/gocov) links in perfectly to your existing tests.   To install gocov, just use your traditional “go get” command: go get github.com/axw/gocov/gocov.  Then you can use the gocov command in place of your “go test” command like so:

gocov test ./...

This will print out a JSON report of coverage so you’ll need to pipe the result to the reporter

gocov test ./... | gocov report
github.com/imosquera/uploadthis/conf/conf.go ParseOpts 100.00% (6/6)
github.com/imosquera/uploadthis/conf/conf.go @49:18 0.00% (0/9)
github.com/imosquera/uploadthis/conf --------- 40.00% (6/15)

This still won’t let you know which lines aren’t covered but you’ll have an idea which methods need work.  Using the “gocov annotate” method will show you the lines that aren’t covered but takes a bit more work.

Visually See Code Coverage

Being able to visually see which lines need some test coverage is critical to knowing where your tests are failing to cover.  Gocov-html (https://github.com/matm/gocov-html) takes the output from the gocov tool and creates a simple html which shows exactly where we’re missing coverage.  Instead of piping the result of the test to gocov report you can use the gocov-html tool:

 

gocov test ./… | gocov-html > index.html

 

We hope that this post helps you to test better in Go.  It’s still a relatively new language and things are changing rapidly.  If you think there is a testing tool we’re overlooking, let us know in the comments.  Additionally here’s a video for our weekly demo day discussing the testing tools described above

 

Leave a Reply