Go testing tricks

I recently wrote a blog post with my point of view about
testing. I used Go as the language to concretize it. I had
good feedback about that article, and this is all about how I write tests in
Go.

Fixtures

I wrote that I don’t like them, but I think they are useful. You can use them
to verify the same function checking the same assertion with different input.
So let’s say you are testing a function that returns the multiplication of two
numbers if the first is even, it returns the division if not.

I will write two tests, one to test events number and one to test the other
case, and I will set up two fixtures one for every test. I won’t write just one
test with elaborate fixtures because they are hard to read and the name of the
test function will help a lot to understand the assertion. Small example good
for blogging purpose. But I hope you got the idea.

packagetestimport"testing"funcMagicFunction(fint,sint)int{iff%2==0{returnf*s}returnf/s}funcTestEventInputsShouldReturnMoltiplication(t*testing.T){table:=[]struct{firstintsecondintresultint}{{2,1,2},{4,10,40},}for_,s:=rangetable{ifr:=MagicFunction(s.first,s.second);r!=s.result{t.Errorf("Got %d, expected %d. They should be the same.",r,s.result)}}}funcTestOddInputsShouldReturnDivision(t*testing.T){table:=[]struct{firstintsecondintresultint}{{15,3,5},{21,7,3},}for_,s:=rangetable{ifr:=MagicFunction(s.first,s.second);r!=s.result{t.Errorf("Got %d, expected %d. They should be the same.",r,s.result)}}}

sub-test

To make the fixtures a bit better I use the t.Run function a lot. It is a
feature introduced in Go 1.9 as part of the testing package.

packagetestimport("fmt""testing")funcMagicFunction(fint,sint)int{iff%2==0{returnf*s}returnf/s}funcTestEventInputsShouldReturnMoltiplication(t*testing.T){table:=[]struct{firstintsecondintresultint}{{2,1,2},{4,10,40},}for_,s:=rangetable{t.Run(fmt.Sprintf("%d * %d",s.first,s.second),func(t*testing.T){ifr:=MagicFunction(s.first,s.second);r!=s.result{t.Errorf("Got %d, expected %d. They should be the same.",r,s.result)}})}}

vim-go has an option let g:go_test_show_name=1 to allow the name of the
test as part of the output for :GoTest or :GoTestFunc. This helps a lot to
enjoy this feature.

Golden files

Golden files are something used in different packages in the Go standard
library, and Michael Hashimoto spoke about it during his brilliant talk about
testing at the GopherCon 2017.
In case of complex output, you can verify the result of the tests with the
content of a file. It improves order and readability. When you declare a
global flag in your test, it becomes available inside go test so if you use
the update flags all the tests will pass, but you will update all the golden
files. So this is very useful if you need to compare a lot of bytes.

update:=flag.Bool("update-golden-files",false,"Update golden files.")

go test-update-golden-files

I was using this trick a lot when I was writing PHP code, and I was testing HTTP responses.

Test helper and return function

When you have repeatable code across tests, you can create a helper function,
and you can use it in your tests. There are two general rules about this
approach:

The helper function should have access to *t testing.T variable.

Your helper never returns an error; it marks the test as failed. That’s why
it needs access to *t. testing.T.

Another good trick is to return a function from the helper to clean up what you
did in the helper. So let’s say that your helper starts an HTTP server. You can
return the HTTP Close function as a callback.

func()testHelperStartHTTPServer(t*testing.T)func(){ts:=httptest.NewServer(http.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){// long and complex mock maybe with a golden file and so on}))returnfunc(){ts.Close()}}funcTestYourTest(t*testing.T){hclose:=testhelperStartHTTPServer()// All your logic and checksdeferhclose()}

I used the same practice when I was writing integration tests using bash and
bats. It is a very clean and easy to
read approach.

parallel

You can use the function t.Parallel() to notify at the test runner that your
case can run in parallel with other tests marked as parallel. When you write
unit tests, you can almost always run them in parallel because they should be
completely isolated.

Short and verbose

-short and -v are two flags available when you run go test. You can use
them in your tests:

testing/quick

testing/quick is a nice package that
offers a set of utilities to write test quick. Go has not an assertion library
inside the stdlib but this can help if you are like me and you are happy to not
vendor assertion libraries because if { } with some sugar is what I need.