Continuous Integration in Go: Ginkgo & Coveralls

I've recently started programming in Go. But you can't really call it programming without:

I took some time to see what was available in Go. I came up with the following:

Unit Tests with Ginkgo

If you've used RSpec (Ruby), you'll love Ginkgo and its matcher library Gomega:

var _ = Describe("Set", func() {
	var set *Set
	BeforeEach(func() { set = NewSet() })

	Describe("adding and removing elements", func() {
		It("adds elements", func() {
			set.Add("Rails")
			set.Add("Django")

			Expect(set.Cardinality()).To(Equal(2))
			Expect(set.Contains("Rails")).To(BeTrue())
			Expect(set.Contains("Django")).To(BeTrue())
		})

		It("removes elements", func() {
			set.Add("iOS")
			set.Add("Android")
			set.Remove("Android")

			Expect(set.Cardinality()).To(Equal(1))
			Expect(set.Contains("iOS")).To(BeTrue())
			Expect(set.Contains("Android")).To(BeFalse())
		})
	})
})

To use it, just go get it using the instructions from the README:

$ go get github.com/onsi/ginkgo/ginkgo
$ go get github.com/onsi/gomega

Continuous Integration

Both drone.io and Travis CI provide CI services for Go. I normally love Travis CI, but found them to be pretty slow when booting a new Go job, so I decided to give drone.io a try. I wasn't disappointed!

Unlike Travis CI and its .travis.yml file, drone.io has you edit your build script online. That means you don't check it into your project. Users can still see the script by accessing your project's drone.io URL.

I can think of pros and cons to this approach, but who cares? drone.io starts jobs crazy fast.

Both Travis CI and drone.io are free for public projects. It's also very easy to setup:

Publishing Code Coverage Metrics

Go was designed with tools in mind, and code coverage is no exception. It's stupid easy to generate code coverage reports:

$ go test -cover

Uploading metrics to coveralls.io is simple as well, thanks to a Go package called goveralls:

$ go get github.com/mattn/goveralls
$ goveralls -service drone.io \
      -coverprofile=example.coverprofile \
      -repotoken $COVERALLS_TOKEN

I encountered one minor snafu, however. You see, you can run all the tests in your current directory, recursively, using:

$ go test ./... -cover

But doing so generates coverage reports in each directory, while goveralls only supports uploading a single report.

So I created a tool called gover. gover collects all of your coverage reports into a single report (called gover.coverprofile by default):

$ go get github.com/modocache/gover
...
$ go test ./... -cover
...
$ gover
$ goveralls -service drone.io \
      -coverprofile=gover.coverprofile \
      -repotoken $COVERALLS_TOKEN

All Together

Putting it all together, I got a CI script that looks something like this:

#!/bin/bash

# go get dependencies
go get code.google.com/p/go.tools/cmd/cover
go get github.com/modocache/gover
go get github.com/mattn/goveralls
go get github.com/onsi/ginkgo/ginkgo
go get github.com/onsi/gomega
go get -v ./...

# test all tests recursively (like `go test ./... -cover`)
ginkgo -r --randomizeAllSpecs -cover

# submit coverage report
gover
goveralls -service drone.io \
    -coverprofile=gover.coverprofile \
    -repotoken $COVERALLS_TOKEN

For an example of a project using this setup, just check out gover itself! For more on Go and testing, check out RESTful Go: An API Server with 90%+ Test Coverage in 260 Lines of Code.