GitHub recently posted about a new GitHub Action that can be used to summarise your test results. The action is called test-summary/action, available at github.com/test-summary/action. There are several examples on how to use the action at github.com/test-summary/examples. However, there were no examples on how to use this with Go. I contributed a pull request which showed how to achieve this. In this post, I will show how to use the action with Go.
Contents
GitHub recently posted about a new GitHub Action that can be used to summarise your test results. The action is called test-summary/action
, available at github.com/test-summary/action. There are several examples on how to use the action at github.com/test-summary/examples. However, there were no examples on how to use this with Go. I contributed a pull request which showed how to achieve this. In this post, I will show how to use the action with Go.
Introducing the test-summary/action GitHub action
I recently spotted the upcoming job summaries capability announced on the GitHub changelog. I’ve been eagerly waiting for this capability to come to the platform.
Fast forward a couple of weeks, and I spotted another post from GitHub on LinkedIn. This post was about a new GitHub Action called test-summary/action
, which appears to be made by Edward Thomson. It’s a simple action that can be used to summarise your test results. It looks as though this action uses the job summary capability that was announced in the GitHub changelog.
Given my recent journey into Go, I thought it would be a good idea to try it out with the Go language. After exploring the examples repository for the test-summary/action
GitHub action, I found that an example didn’t yet exist for Go. I decided to create one.
The test-summary/action
action can summarised test results based on the JUnit XML or the TAP test output. This immediately got me thinking, is it even possible to use the action with Go?
Spoiler alert, the answer is yes - Though we need to convert to one of the other formats first. Let’s first explore the options.
Options to output Go tests to alternate formats
Typically, Go tests are executed by using the go test
command. As far as I’m aware, this command is typically coupled with additional bash commands to output the contents to a file.
There is no capability to output the test results directly to JUnit XML or TAP test output format from the default testing package. This then started a trail of research. What are the options to convert the test output to a format that can be used with the action?
For the TAP test output - I stumbled upon this GitHub repository, though it looks like the project is archived / read only. Unfortunately, I couldn’t find any alternative shims / examples which convert from the default Go test format into TAP.
However, the JUnit XML format proved to be more promising. I quickly found the go-junit-report GitHub repository. This is a library that can be used to convert the Go test output format into the JUnit XML output (making it usable with the action).
Using the go-junit-report
To use the go-junit-report library, you first need to install it. This is done by running the following command (which will be very familiar if you’ve installed any other Go tools).
go install github.com/jstemmer/go-junit-report@latest
Great, you now have the go-junit-report library installed. Next up, you’ll need to pipe the output of the go test
command into the go-junit-report
library.
go test -v 2>&1 ./... | go-junit-report -set-exit-code > report.xml
Let’s break that line down a little further. Let’s first look at the go test -v 2>&1 ./...
portion of the command.
First up, we’re running the go test
command. This will run all of the tests in the current directory. The -v
flag will output the test output in a verbose format.
The 2>&1 is a redirection operator. This is used to redirect the standard error to the standard output. The redirection operator is useful when you want to capture the output of a command and then redirect it to a file, or some other output.
Finally, the ./… is a wildcard. This is used to indicate that you want to run all of the tests in the current directory.
Now let’s look into the go-junit-report -set-exit-code > report.xml
part.
Firstly, the go-junit-report
library is used to convert the test output from the Go test output into the JUnit XML format.
The -set-exit-code
flag is used to set the exit code to 1 if any tests failed (as per the README file from the repository). This is used to show that the job failed. This is critical in the context of GitHub actions (i.e. ensuring that this step in the job fails, to prevent progressing any further steps).
Finally, the > report.xml
part is used to redirect the output of the go-junit-report
library to the report.xml
file.
At this point, you likely see the direction we’re heading. We need to take the above steps and use them directly within a GitHub Action workflow.
Using the go-junit-report library within a GitHub Action
I’ve recently been working on a project in private GitHub repository. I’m building a multi-tenant SaaS application. To achieve this, i’m using Azure Container Apps, Dapr, KEDA and Go as my primary toolset. I am building the individual microservice using a Hexagonal Architecture, which lends itself well for testing.
Fortunately, I had already written a test suite for each of the microservices. Better yet, the tests were already being executed in a GitHub Action workflow. This was part of my development lifecycle, to determine whether the code was working as expected before building a new version of the docker image.
As I already had the testing framework set up, I thought this could be an easy project to tweak, so that I can summarise the test results back into the GitHub Action workflow run.
Below, you can see the resulting GitHub Action workflow that I created -
name: 'Publish Tenant Container Image'
on:
push:
branches:
- main
paths:
- 'api/tenant/**'
pull_request:
branches:
- main
paths:
- 'api/tenant/**'
env:
AZURE_ACR_NAME: cwceventplatform
AZURE_ACR_ENDPOINT: cwceventplatform.azurecr.io
IMAGE_NAME: eventplatform/tenant
jobs:
tests:
runs-on: ubuntu-latest
env:
MICROSERVICE_LOCATION: api/redirect/
steps:
- name: 'Checkout GitHub Action'
uses: actions/checkout@v3
- name: Generate Mocks and Run Tests
working-directory: ${{ env.MICROSERVICE_LOCATION }}
run: |
export GOPATH="$HOME/go/"
export PATH=$PATH:$GOPATH/bin
go install github.com/golang/mock/mockgen@v1.6.0
go install github.com/jstemmer/go-junit-report@latest
go generate ./...
go test -v ./... | go-junit-report -set-exit-code > report.xml
- name: Test Summary
uses: test-summary/action@v1
with:
paths: |
${{ env.MICROSERVICE_LOCATION }}/report.xml
if: always()
build-and-deploy-image:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs:
- tests
permissions:
id-token: write
contents: read
environment: production.build
runs-on: ubuntu-latest
env:
MICROSERVICE_LOCATION: api/tenant/
steps:
# checkout the repo
- name: 'Checkout GitHub Action'
uses: actions/checkout@v3
- name: 'Download swagger CLI'
run: |
sudo apt update
sudo apt install -y apt-transport-https gnupg curl
curl -1sLf 'https://dl.cloudsmith.io/public/go-swagger/go-swagger/gpg.2F8CB673971B5C9E.key' | sudo apt-key add -
curl -1sLf 'https://dl.cloudsmith.io/public/go-swagger/go-swagger/config.deb.txt?distro=debian&codename=any-version' | sudo tee /etc/apt/sources.list.d/go-swagger-go-swagger.list
sudo apt update
sudo apt install swagger
- name: 'Generate Swagger JSON'
working-directory: ${{ env.MICROSERVICE_LOCATION }}
run: |
swagger generate spec -o cmd/httpserver/swagger/swagger.json
- name: 'Az CLI Login via OIDC'
uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: 'Build and Push Tenant Container Image'
working-directory: ${{ env.MICROSERVICE_LOCATION }}
run: |
az acr login --name ${{ env.AZURE_ACR_NAME }}
docker build . -t ${{ env.AZURE_ACR_ENDPOINT }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
docker push ${{ env.AZURE_ACR_ENDPOINT }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
deploy:
needs:
- build-and-deploy-image
permissions:
id-token: write
contents: read
environment: production.deploy
runs-on: ubuntu-latest
env:
CONTAINERAPP_NAME: ep-tenant
CONTAINERAPP_RG: event-platform-rg
steps:
- name: 'Az CLI Login via OIDC'
uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: 'Deploy Tenant Container Image'
run: |
az extension add --name containerapp
az containerapp update -n ${{ env.CONTAINERAPP_NAME }} -g ${{ env.CONTAINERAPP_RG }} --image ${{ env.AZURE_ACR_ENDPOINT }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
There’s quite a lot going on there. In the above workflow, you will see that I have a tests
job, which tests the code before a container image is built or released. Let’s focus in on that section.
jobs:
tests:
runs-on: ubuntu-latest
env:
MICROSERVICE_LOCATION: api/redirect/
steps:
- name: 'Checkout GitHub Action'
uses: actions/checkout@v3
- name: Generate Mocks and Run Tests
working-directory: ${{ env.MICROSERVICE_LOCATION }}
run: |
export GOPATH="$HOME/go/"
export PATH=$PATH:$GOPATH/bin
go install github.com/golang/mock/mockgen@v1.6.0
go install github.com/jstemmer/go-junit-report@latest
go generate ./...
go test -v ./... | go-junit-report -set-exit-code > report.xml
- name: Test Summary
uses: test-summary/action@v1
with:
paths: |
${{ env.MICROSERVICE_LOCATION }}/report.xml
if: always()
This job performs the following steps -
- Performs a checkout of the code from GitHub
- Generates the needed mocks using
mockgen
which are used within the tests (based on annotations within my codebase) - Runs the tests, and converts the output to a JUnit report which is used to generate a test report
- Generate a test summary, which is displayed as a job summary in the GitHub UI
And, it really is as simple as that, as you can see from the screenshot below!
Conclusion
It was fun to play around with my existing GitHub Actions workflow, to identify an approach to summarise my Go test results. I’m thrilled that this ultimately lead to a contribution back into the community. I continue to progress along my own Go learning journey, but I hope that this will be a useful contribution and can be used to build a better experience for developers.
Are you building your Go code using GitHub Actions? Do you think that this might be beneficial in your own workflow? Why not give it a go, and drop a comment below to let me know how you get on? Finally - it goes without saying - A big thank you to Edward Thomson for making this brilliant GitHub action.
Until the next one, thanks for reading and bye for now!
Related
In this post, I’ll be talking about how to use interfaces in Go. This is a continuation of my learning using the Go language. I’ll use interfaces to create an application that interacts with several types of bank accounts.
Blog
I’ll be transparent. The purpose of this post is to help with my own understanding of the Go & and * operators. It’s going to be a very short post, and I’m going to try to explain the concepts in a way that I can understand. I’ve used these operators in C previously, but whenever I’m using them - I always end up having to remember the syntax / which operator is which / what they do. For whatever reason, it doesn’t always come intuitively to me.
Blog
ToolUp Tuesday is all about showing the thought process and decisions made when creating an application. Join Chris and Matt as they build a new application from the ground up, including development processes, tooling, service choices and architectural decisions!
Episode