Let's Automatically Run Golang Tests and Generate Reports with Github Actions When Opening a PR
Automating builds or tests for any language, anywhere, is crucial.
Although it’s possible to run tests locally and push them, sometimes people push without running tests. Hence, relying less on local tests and more on automation can be beneficial for long-term use and maintenance.
In this post, we’ll explore how to create an action for testing in Golang and how to automatically run tests and generate reports when a PR is opened.
The scenario is designed as follows:
- Set up Go
- Run Go Tests
- Generate Test Report
- Leave a report link as a comment in the PR
Test Code
Go Code
package math
import "errors"
var (
ErrDivideByZero = errors.New("cannot divide by zero")
)
func Divide(a, b int) (int, error) {
if b == 0 {
return 0, ErrDivideByZero
}
return a / b, nil
}
The code implements a function called Divide()
. It simply divides a
by b
, and since dividing by zero is not allowed, it returns an error.
Test Code
package math
import "testing"
func TestDivide(t *testing.T) {
type args struct {
a int
b int
}
tests := []struct {
name string
args args
want int
wantErr bool
}{
{
name: "Divide 10 by 2",
args: args{
a: 10,
b: 2,
},
want: 5,
wantErr: false,
},
{
name: "Divide 10 by 0",
args: args{
a: 10,
b: 0,
},
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Divide(tt.args.a, tt.args.b)
if (err != nil) != tt.wantErr {
t.Errorf("Divide() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("Divide() got = %v, want %v", got, tt.want)
}
})
}
}
You can test locally with go test -v ./...
, but to perform tests on Github Actions, you’ll need to write a yaml file.
Github Actions
name: Go Test and Coverage
on:
pull_request:
types: [ opened, synchronize, reopened ]
workflow_dispatch:
jobs:
test:
name: Run Go Tests
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.23
- name: Test Report
id: test-report
uses: robherley/go-test-action@v0
with:
testArguments: ${{ github.event_name == 'pull_request' && '-short -v ./...' || '-count 1 -v ./...' }}
- name: PR Comment
if: always() && github.event_name == 'pull_request'
uses: mshick/add-pr-comment@v2
with:
message: |
${{ steps.test-report.outcome == 'success' && '' || '' }}
**📃 Report**: https://github.com/${{github.repository}}/actions/runs/${{ github.run_id }}
This code is written and tested by me, so you can use it as is.
Explanation:
- name: Checkout Repository
uses: actions/checkout@v4
This step checks out the repository. It is similar to the git clone
command performed locally and retrieves the repository code to the action runner’s file system.
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.23
This step installs Go. You could use apt-get
to install it, but using Setup actions usually applies action cache, thus speeding up the build process.
- name: Test Report
id: test-report
uses: robherley/go-test-action@v0
with:
testArguments: '-count 1 -v ./...'
You can create an action for generating test reports, but since there is already a good action available, we decided to use it.
I used the robherley/go-test-action. This action runs Go tests and logs the report in Summary.
Additionally, I applied testArguments
, but it’s optional. If not specified, -v ./...
is the default. I used the count 1
option to prevent caching.
Refer to previous Go Test cache related post.
- name: PR Comment
if: always() && github.event_name == 'pull_request'
uses: mshick/add-pr-comment@v2
with:
message: |
${{ steps.test-report.outcome == 'success' && '' || '' }}
**📃 Report**: https://github.com/${{github.repository}}/actions/runs/${{ github.run_id }}
This post leaves a report link as a comment in the PR. Since the comment is left only when a PR is present, the condition if: always() && github.event_name == 'pull_request'
is applied.
always()
ensures execution regardless of the success or failure of previous actions. Shields.io is used to display badges for success or failure nicely. (It’s nothing major, it’s just a decorative icon)
Success
Failure
Result
Let’s look at the actual results created.
On Success
Check the comments on this PR.
Clicking on the report shows the detailed report below.
On Failure
Check the comments on this failed PR.
Clicking the link shows the failed report below.
Summary
The report is adequately detailed, and with some customization of PR comments, it can be quite useful.
Of course, there are numerous other actions. Some may send alerts to Slack, or want more varied reports and forms.
In such cases, you may create your actions or modify existing ones slightly for use.
+ Setting Github Ruleset
Simply setting up the action isn’t enough; there’s always a chance it might be merged even if it fails.
The
Merge pull request
button is always clickable.
In such scenarios, using Github’s Ruleset is helpful. It can block merges based on the results of certain actions.
First, navigate to Repository - Settings - Rules - Rulesets
.
Select New ruleset - New branch ruleset
. (Choose New tag ruleset
if not using for PRs)
Name it as desired and add the target branch for merging under Target branches
.
For me, the target is the main
branch.
Change the Enforcement Status to Active. This activates the created Ruleset.
Choose Required status checks to pass
.
Click the Add checks +
button and add Run Go Tests
, which was the Job name.
You’ll now see the added check on the list. Create the Ruleset by clicking the Create button.
Now, unlike before, the Merge pull request
button is inactive.