Recently I’ve been working on a small Go library.

I wrote unit tests to cover most of the major functionality.

I got code coverage 33%

And I became suspicious.

Why 33%?

Because go test -race -coverprofile=coverage.out ./... includes all auto-generated Fake objects used by unit tests in the calculation. Depending on how many and how large the fake objects are, real code coverage is reduced by that much. After removing fakes from the calculation, code coverage was raised to 62%!

How we measure code coverage in Gitlab CI

1
2
3
4
5
6
7
unit tests:
  image: golang:1.22.2
  stage: test
  script:
    - go test -race -coverprofile=coverage.out ./...
    - go tool cover -func=coverage.out
  coverage: '/total:\s+\(statements\)\s+(\d+.\d+\%)/'

This will include all Fake objects code and will reduce the real code coverage of our tests.

How it should be

If we want to use go tool cover and our fake files start with the prefix fake_ (i.e. counterfeiter):

1
2
3
4
5
6
7
8
unit tests:
  image: golang:1.22.2
  stage: test
  script:
    - go test -race -coverprofile=coverage.out.tmp ./...
    - cat coverage.out.tmp | grep -v "fake_" > coverage.out
    - go tool cover -func=coverage.out
  coverage: '/total:\s+\(statements\)\s+(\d+.\d+\%)/'

If we use a more recent Go version it can be done simpler, no matter what fake library we use:

1
2
3
4
5
6
unit tests:
  image: golang:1.22.2
  stage: test
  script:
    - go test -race -coverprofile=coverage.out ./...
  coverage: '/\s+coverage:\s+(\d+.\d+\%)/'

Note that the coverage regular expression is slightly different.

Conclusion

If you made the same mistake, you must know that your code coverage is much higher than you, and everybody else think it is :)

Cheers!