It's not uncommon to find yourself in a situation where you would like to make an exception for some code from being included in code coverage; perhaps it's auto-generated code or perhaps it's a wrapper of a third party API. Unfortunately, unlike many other languages, there is no built-in way to do this in Go (Hopefully not for too long). In this article, we go over a simple workaround to achieve this goal.
First things first, a quick recap. Code coverage can be generated using the go cover
tool. From the docs, usage of the tool is described as:
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
As such, we first run our tests using:
go test ./... -coverprofile=coverage.out
Subsequently, we generate code coverage using:
go tool cover -html=coverage.out
I like to run the commands one after the other in one call, like so:
go test ./... -coverprofile=coverage.out && go tool cover -html=coverage.out
The workaround
It's simple. Code coverage is generated based on the coverprofile
which is no more than a plain text file annotating which lines of each code file are covered and which are not. Remove all lines about a file from the coverprofile
and it won't be included in the code coverage report. As simple as that.
Here is what a coverprofile
looks like
It wouldn't be ideal if we have to manually manipulate the coverprofile
every time we want to generate code coverage. A better way would be to automate this process. For that, We need to do 2 things. The first is a file that holds paths to files to be excluded from code coverage. File paths in this file should include the full package name. I created a file named "exclude-from-code-coverage.txt". Here is a snippet of what it looks like
The second is a script that removes files in exclude-from-code-coverage.txt from the coverprofile
. This simple script (named exclude-from-code-coverage.sh) does just that:
#!/bin/sh
while read p || [ -n "$p" ]
do
sed -i '' "/${p//\//\\/}/d" ./coverage.out
done < ./exclude-from-code-coverage.txt
And finally, tying it all together:
go test ./... -coverprofile=coverage.out && ./exclude-from-code-coverage.sh && go tool cover -html=coverage.out
Let's break it down:
- The first command,
go test ./... -coverprofile=coverage.out
runs the tests and generates thecoverprofile
. - The second command,
./exclude-from-code-coverage.sh
, removes files we want to exclude from code coverage from thecoverprofile
. - The third command,
go tool cover -html=coverage.out
, generated the code coverage report.
Top comments (5)
One last thing to note, that I find interesting, is that one way to exclude a file from coverage natively would be to not have tests on the whole package. What I mean by this is: if you have a package called api and you have 3 files under this package, api_one.go, api_two.go and api_three.go. If you have no test files, then package
api
will not be counted towards the overall code coverage nor will it be considered to have 0% coverage. However, if you add a test file api_one_test.go, then your package will be included in the overall coverage percentage and it will have a coverage percentage value.go test -count=1 -coverprofile coverage.out -coverpkg=./... ./...
cat coverage.out | grep -v "mock" | grep -v "othername" > coverage.final.out
this:
then you can create a .covignore file and place wildcards in there
e.g.
.test.
We want a 1-liner, and so it seems simpler, instead of running for ./... then filtering down with a separate script, to exclude /test and /test/mock directories like this:
For ease-of-use we have a Makefile (who remembers all these commands anyway?) so the developer can type
make coverage
. These targets take care of it:go test ./store/... ./server/... -race -v -coverprofile="coverage.out"
where store and server are 2 packages.
Note: "..." is to add subfolders as well.