Blog Post

Getting Started with Code Coverage for Golang

February 17, 2021 Tom Hu

Golang, developed in 2009, is one of the most popular programming languages in the world. At Codecov, it currently sits as the language of choice for over 14% of our open-source repositories, or as the 3rd most widely used language. This blog post will show you how to get a simple Golang project up and running with tests and code coverage with Codecov. You can view the entire source code of this post at our example repository.


Getting started with Goloang

We will start with a simple calculator app that exposes two functions: Add and Subtract. Both functions accept two integer arguments and return an integer. We can paste the code below into a file called calculator.go.

calculator.go

package calculator

func Add(x, y int) int {
  return x + y
}

func Subtract(x, y int) int {
  return x - y
}

Now, let’s add a test for our Add function. We can take advantage of Go’s built-in testing libraries to do this. Open a new file called calculator_test.go

calculator_test.go

package calculator

import "testing"

func TestAdd(t *testing.T) {
  if got, want := Add(1, 2), 3; got != want {
    t.Errorf("add method produced wrong result. expected: %d, got: %d", want, got)
  }
}

The test file will call the Add function and compare its output with our expected value of 3. The test helps ensure that if we change the function later, it will still work as expected.

 

Run your tests and get coverage locally

You can run the test locally by calling go test This should bring up the following successful output.

--> go test
PASS
ok  	{{ SRC_DIRECTORY }}	0.178s

We can, again, use Golang’s built-in coverage tools to start collecting code coverage

--> go test -race -covermode=atomic -coverprofile=coverage.out
PASS
coverage: 50.0% of statements
ok  	{{ SRC_DIRECTORY }}	0.160s

Although covermode and race are not required parameters, they are highly recommended by the Codecov team. coverpofile ensures that we collect coverage, and the metrics will be saved to the file specified, in our case, coverage.out.

 

How to upload to Codecov

Now that we are able to run coverage locally, we can begin uploading these reports to Codecov. We will be taking advantage of Codecov’s bash uploader to do this. We will not go into detail about setting up a pipeline, but Codecov will work with any CI/CD. Below, we show you basic configurations for GitHub Actions and CircleCI.

GitHub Actions

name: Test and coverage

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 2
      - uses: actions/setup-go@v2
        with:
          go-version: '1.14'
      - name: Run coverage
        run: go test -race -coverprofile=coverage.out -covermode=atomic
      - name: Upload coverage to Codecov
        run: bash <(curl -s https://codecov.io/bash)

CircleCI

version: 2.1

jobs:
  build:
    docker:
      - image: circleci/golang:1.15
    steps:
      - checkout
      - run:
          name: Run tests and coverage
          command: go test -race -coverprofile=coverage.out -covermode=atomic
      - run:
          name: Upload coverage to Codecov
          command: bash <(curl -s https://codecov.io/bash)

workflow:
  version: 2.1
  build-test:
    jobs:
      - build

You will notice that both configurations call bash <(curl -s https://codecov.io/bash) which will handle uploading the report to Codecov.

If we commit these files to a pull request and run our CI, we expect Codecov to comment on our PR with coverage information like below:

Covering uncovered code

You will notice that our calculator.go file is only 50% covered which matches with our local coverage. So how can we see what lines aren’t covered? We can click the report from the comment to browse our report

The light green background indicates that all of the above code was added. The darker green bar shows us that that line has been covered by tests, while the red is still uncovered. This makes sense as we only wrote a test for our Add function. The Subtract function still needs a test.

We can add the following code to our test file

calculator_test.go

func TestSubtract(t *testing.T) {
  if got, want := Subtract(3, 2), 1; got != want {
    t.Errorf("Subtract method produced wrong result. expected: %d, got: %d", want, got)
  }
}

After committing the above snippet, we see that Codecov shows us that all of our code is covered.


Now that you have uploaded your first coverage report to Codecov, you can upgrade your code coverage by configuring Codecov settings, adding flags, or viewing the sunburst graph to identify code areas in need of test coverage. If you have or are interested in a more complex setup, please reach out to us on our community boards.