chore: add test coverage reporting for golang tests
What does this MR do and why?
This MR adds test coverage reporting for our Go tests by integrating gotestsum for test execution and gocover-cobertura for generating GitLab-compatible coverage reports.
The pipeline configuration follows GitLab's official documentation: https://docs.gitlab.com/ci/testing/code_coverage/#configure-coverage-reporting
The existing make test target now supports an optional COVERAGE parameter. When set to true, it generates detailed coverage reports. This allows developers to skip coverage generation during local development for faster test iterations while ensuring CI pipelines capture full coverage data.
Test coverage will help us quickly identify which parts of the codebase lack associated tests, improving code quality and review efficiency.
References
Please include cross links to any resources that are relevant to this MR. This will give reviewers and future readers helpful context to give an efficient review of the changes introduced.
- Docs for adding coverage: https://docs.gitlab.com/ci/testing/code_coverage/#configure-coverage-reporting
- gotestsum: https://github.com/gotestyourself/gotestsum
- gocover-cobertura: https://github.com/boumenot/gocover-cobertura
How to set up and validate locally
- Install the dependencies:
make deps
- Run the tests with coverage for any environment:
COVERAGE=true ./env.sh postgres make test
- View the coverage report in your browser:
open coverage/coverage.html
The artifacts from the job can also be verified by going to the JOB link and browsing the artifacts here: https://gitlab.com/gitlab-org/cells/topology-service/-/jobs/11772057846/artifacts/browse/coverage/
Alternative
While common-ci-tasks provides a go-unittests.yml template, I decided against using it due to our specific requirements.
The Challenge: We need to run unit tests across three different environments (null, spanner, postgres), each requiring different service configurations. This creates integration challenges with the template.
Evaluated Approaches
Option 1: Matrix with All Services
go_unittests:
services:
- spanner
- postgres
parallel:
matrix:
- TEST_ENV: [null, spanner, postgres]
-
❌ Starts unnecessary services for each environment (e.g., spanner service running for postgres tests) -
❌ Wastes CI resources significantly
Option 2: Four Separate Jobs with Template
go_unittests: # Base job from template
rules:
- when: never # Must manually disable
.test:
extends: go_unittests
<<: *default-rules # Re-enable for child jobs
test with-null:
extends: .test
test with-spanner:
extends: .test
services: [spanner]
test with-postgres:
extends: .test
services: [postgres]
-
❌ Requires disabling the base template job (workaround) -
❌ Adds complexity with nested extends -
❌ Less maintainable configuration
The reason why I decided to duplicate the configuration is because:
make test for fast iteration, or COVERAGE=true make test to see coverage locally before pushing
gotestsum, gocover-cobertura)
./env.sh wrapper pattern doesn't align with the template's design (though we could update the template to support it if needed)