Have you ever wanted to separate your unit tests from your integration or smoke tests? Go has a built-in mechanism for allowing you to logically separate your tests, in addition to adding conditionals (e.g. operating system or CPU architecture) with Build Tags.

What are Build Tags

Build tags are a directive provided, in the form of a single line comment, at the top of a .go source file that tells the Go compiler important information about how it should deal with that file when go build is run.

For example, the following file will only be included in the build if the GOOS=linux environment variable is set:

// +build linux

package mypackage

...

The build tag needs to be placed as close to the top of the file as possible, and must have a blank line beneath it.

Tags can also be negated with the ! operator. E.g. A file with // +build !linux will be included in all builds that aren’t on linux.

Tags can contain multiple conditions. If the conditions are on the same line then they are evaluated as an OR condition. If they’re on separate lines they’re evaluated as an AND condition.

// +build linux darwin
// +build amd64

package mypackage

...

The above example file will only be included in linux/amd64 and darwin/amd64 builds.

Separating Tests

Build tags can also be used to separate different types of tests, such as unit and integration tests, within separate _test.go files.

Let’s continue with the example of unit and integration tests.

myfile_test.go:

package mypackage

import "testing"

...

myfile_integration_test.go:

// +build integration

package mypackage

import "testing"

...

There are two file examples shown above, the myfile_integration_test.go file contains a build tag of // +build integration, while the myfile_test.go file, where unit tests will be stored, contains no build tag.

When running go test in the directory where these files are stored the Go test runner will only run the tests in myfile_test.go. To run the tests within myfile_integration_test.go the --tags flag will need to be provided - go test --tags=integration.

There is one small problem though. Those following along probably would have picked up on this already. When running go test --tags=integration the Go test runner still runs the unit tests! To avoid this you can add a negation to the non-integration tests files, like so:

myfile_test.go

// +build !integration

package mypackage

import "testing"

...

With the above build tag included in all unit test files, the Go test runner will continue to run the tests when go test is used, but they will be excluded when running go test --tags=integration.

Why?

There are plenty of reasons developers might want to separate tests. Some simple example might be:

  • Integration tests are often slower, so you may want to only run them after the unit test (which are often much faster) have passed.
  • Smoke tests that are run against the live application, generally after a deployment.
  • Deploying the same app to different tenants.

Of course, this is by no means an exhaustive list!

Conclusion

Build tags are a very useful tool within the Go ecosystem, and there are plenty of other possibilities above and beyond this one example. Head over to the go/build package documentation for more information!

Thanks for reading.