Writing packages in Go using test-driven development #screencast

A package is a folder of Go files that make up an importable collection of types, functions, variables, and constants that are reusable across Go programs. The standard library comes with a variety of such packages providing a wide range of capabilities from JSON encoding, through MD5 hashing, HTTP servers and clients, templating and support for compression file formats like zip and tar. The full list of standard packages is available on the Go website. But things get really interesting when we look at the vast selection of open-source and community contributed packages that can give the most basic of projects a kick-start, and save us from hours of development.

Packages are a staple of the Go community, and the best ones come with evidence that they work.

Evidence that a package works usually comes in the form of unit tests, which were ideally written before the actual package code itself. This tutorial shows how to write a simple Go package using TDD (test-driven development) practices, writing unit tests first before using the compiler and test tool chain to help you develop your package.

Use case

Go is great for building APIs, but responding to clients can be a verbose business. Rather than repeating ourselves in every `http.HandlerFunc`, this tutorial will show how we might start to put together a package that solves this common problem for us.

Screencast

Writing packages in Go using test-driven development

Summary

Writing a test first means you become the first user of your package, and allows you to focus on keeping your API as small and simple as possible.

Tests are automatable, and in Go that means they can be built and run extremely quickly and frequently; like every time you save a file (see below.)

Proving bugs with a unit test is a great way to communicate intent as well be sure that the bug is fixed (becuase the test passes). Packages built this way are protected from regression so the same bug cannot reemerge later, since there’s a test covering it.

Why not write tests for your colleagues or fellow collaborators to explain with no doubt what you need their code to do. And get them to do the same for you. It’s a great way of distance-pairing and knowledge transference.

Red-green testing

Seeing tests fail before making them pass is the only way to be sure that your implementation code is having an impact. This is called red-green testing, due to the fail/pass/fail/pass nature of development.

I once had a non-technical boss who called me into a meeting becuase he kept seeing the test failures over my shoulder — and he thought I was struggling.

Seeing a test fail means you know you have to change something in order to add value. If you write a test that can never fail, you have managed to create truly useless code. Consider:

assert.True(true) // brilliant

As a high-level overview, the process for red-green TDD is:

  1. Write a unit test
  2. Build and run tests
  3. Notice errors, and do the minimal amount of work to fix them (like missing functions, or incorrect signatures)
  4. Notice the test is failing (red)
  5. Write the smallest amount of code to get the test to pass (green)
  6. Rince, and repeat

On-save functionality for Go

If you want the `on save` functionality featured in the screencast, get your IDE to run this bash command every time you hit save:

if [ -f onsave.sh ]; then ./onsave.sh; else gofmt -s -w ./ && go build . errors && go test -i && go test && go vet && golint && godo -pattern=*.*; fi
  • This has the added benefit of first checking if there is an `onsave.sh` file in the package directory, which it will prefer to this default. This is useful for cases where your tests do actually take a significant amount of time or resources to run, and doing so every time you save a file is not suitable.
  • For Windows, you’ll need to run the same commands with BATCH or something similar.

In Sublime Text, hit `Cmd+., Cmd+5` and add the following declaration to your settings file:

“on_save”: [{
“cmd”: “gs9o_run_many”, “args”: {
“commands”:[
[“clear”],
[“sh”, “if [ -f onsave.sh ]; then ./onsave.sh; else gofmt -s -w ./ && go build . errors && go test -i && go test && go vet && golint && godo -pattern=*.*; fi”]
],
“focus_view”: false
},
"fmt_cmd": ["goimports"]

You’ll need to make sure you have installed `go vet`, `golint`, `godo` and `goimports`.

Go Programming Blueprints #shamelessplug

Learn more about the practicalities of Go with my book Go Programming Blueprints.