Test Metrics.

The SeaLights test metrics guide
for better and faster CI/CD

Go Code Coverage

Go is one of the fastest growing programming languages. It is open source, freely available, and backed by Google. The popularity of Go can be gauged from the fact that it is now used in projects like Docker, Kubernetes and many more. Obviously, developers are moving towards learning and using Go in their day to day activities.

Testing and measuring code coverage is one of the most important aspects of writing good, maintainable code. The Go team has been prudent in offering toolings around Go, such as gofmt (to format source code), godoc (to generate documents) and others.

In a similar spirit, the Go team provided cover tool in release 1.2. Since then it has been an important part of the Go toolchain. Most of the third party coverage tools are built around Cover as well. So, first let us see how Cover works.

Cover

Most of the coverage tools work on basic idea of instrumenting the binary and then analyzing the coverage. Such instrumentation generally adds breakpoints at branches executed by the binary and finds which branches were executed.

Cover takes a different approach. It rewrites a package’s source code before compilation to add instrumentation code. It then compiles and runs the modified source, and dumps the statistics to an output file. The rewriting is easy to arrange because the go command controls the flow from source to test to execution.

The source level approach to test coverage also makes it easy to instrument the code in different ways. For instance, we can ask not only whether a statement has been executed, but how many times. The go test command accepts a covermode flag to set the coverage mode to one of three settings:

  • set: did each statement run?
  • count: how many times did each statement run?
  • atomic: like count, but counts precisely in parallel programs

The default is ‘set’. The atomic setting is needed only when accurate counts are required when running parallel algorithms. It uses atomic operations from the sync/atomic package, which can be quite expensive. For most purposes, though, the count mode works fine and, like the default set mode, is very cheap.

To check for test coverage using the Cover command, navigate to the directory with the package to analyze and run


   $go test -coverage
  

You can also generate coverage profile using Cover tool

$ go test -coverprofile=coverage.out

To analyze coverage via text prompt use

$ go tool cover -func=coverage.out

To analyze coverage via a browser, you can also use

$ go tool cover -html=coverage.out

Here are various options supported by Cover command

Usage of 'go tool cover':

Given a coverage profile produced by ‘go test’:

go test -coverprofile=c.out

Open a web browser displaying annotated source code:

go tool cover -html=c.out

Write out an HTML file instead of launching a web browser:

go tool cover -html=c.out -o coverage.html

Display coverage percentages to stdout for each function:

go tool cover -func=c.out

Finally, to generate modified source code with coverage annotations

(what go test -cover does):

go tool cover -mode=set -var=CoverageVariableName program.go

Flags:

 -func string

    output coverage profile information for each function

 -html string

    generate HTML representation of coverage profile

 -mode string

    coverage mode: set, count, atomic

 -o string

    file for output; default: stdout

 -var string

    name of coverage variable to generate (default “GoCover”)

 Only one of -html, -func, or -mode may be set.

go-acc

One of the frequently raised issues with the cover tool is that it only records coverage for a package that is currently tested, not for all the packages from the project. This causes limited visibility of test coverage.

go-acc provides a cross-platform tool that loops through all the packages and merges all the test coverage outputs into one file. Use go-acc as shown below

# Download and install go-acc

$ go get -u github.com/ory/go-acc

# Run it against one package

$ go-acc github.com/some/package

# Run it against current package

$ go-acc.

# Run it against all the packages in current directory

$ go-acc ./…

gocov

This is a coverage reporting tool for The Go Programming Language. Install gocov using

$ go get github.com/axw/gocov/gocov

gocov supports four commands that help visualize and manage tests. First one is:

$ gocov test [args...]

In the background, this runsgo test [args…] with an implicit >-cover profile added. The output is the result of gocov convert with the profile.

Next command is:

$ gocov convert 

This will convert a coverage profile generated by go tool cover to gocov’s JSON interchange format.

$ gocov report 

This generates a textual report from the coverage data output by gocov convert. Output from gocov test is printed to stdout so you can simply pipe the output to gocov report to view a summary of the test coverage

$ gocov annotate <package[.receiver].function>

gocov also helps you find which lines are not covered. Above command generates a source listing of the specified function, and annotates it with coverage information. This has information such as which lines have been missed.

Summary

In this post we discussed code coverage for one the fastest growing programming languages, Go. Though Go is relatively new it has already gained a lot of interest among developers and much of it is because of Go toolchain. With tools like gofmt, godoc, vet and cover. Since we are focusing on coverage, we learned about the cover command in detail and saw how Go takes the new approach to instrument source code in a platform agnostic manner.

Later on, we saw third-party packages called gocov and go-acc. These packages provide additional testing features like multi-project coverage analysis and easy to manage reports.