Automate Unit tests using Github Actions

Setup unit tests in Github Actions

When we push changes to Github and raise a pull request, we want to make sure that we did not break our existing code and all our unit tests are passing. To ensure this, we can setup a workflow using Github Action to check if all our unit test cases in our app have passed or not.

To create a workflow using Github Actions, create .github/workflow directory in the root directory of the app.

Create a unit_testing.yml file which will contain the workflow.

Directory

Workflow Setup

Let us first give our workflow a name

name: Unit Tests

Now, we want to define the trigger that will run our workflow. We want our workflow to run when a pull request is opened, reopened, or synchronized (new commits pushed).

name: Unit Tests

on:
  pull_request:
    types:
      - opened
      - synchronize
      - reopened

Next, we define the job that will run this workflow. We define the name of the job and the runner that our job will be executed on. In our case, we will use ubuntu-latest as that is sufficient for our use case.

name: Unit Tests

on:
  pull_request:
    types:
      - opened
      - synchronize
      - reopened

jobs:
  unitTest:
    name: Run unit tests
    runs-on: ubuntu-latest

Now, we define all the steps required for our workflow. The first step, is to checkout our repository. We can achieve this using the actions/checkout action. This will checkout the branch for which the pull request is raised.

name: Unit Tests

on:
  pull_request:
    types:
      - opened
      - synchronize
      - reopened

jobs:
  unitTest:
    name: Run unit tests
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

The second step is to setup Java so that it can execute our code. You can use the Java version of your choice. We will be using temurin. It has several benefits like

  • Has consistent and reliable releases
  • Works consistently across different operating systems
  • Widely adopted in CI/CD pipelines
  • Good integration with GitHub Actions
name: Unit Tests

on:
  pull_request:
    types:
      - opened
      - synchronize
      - reopened

jobs:
  unitTest:
    name: Run unit tests
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        
      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '21'

The final setup is to actually execute our unit test cases. You can replace the gradle command with your platform specific command to execute the test cases.

name: Unit Tests

on:
  pull_request:
    types:
      - opened
      - synchronize
      - reopened

jobs:
  unitTest:
    name: Run unit tests
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '21'

      - name: Unit tests
        run: |
          bash ./gradlew test          

That is it. Push these changes to Github and the next time you raise a pull request, this workflow will run.

Common issues

Here are some of the most common issues for the action to not trigger after raising a pull request.

  • Branch Protection Rules: Check if your branch protection rules prevent or block the action.
  • Incorrect workflow directory: The workflow file not being in the correct directory.
    /workflows/unit_testing.yml             # ❌ Wrong
    /.github/workflow/unit_testing.yml      # ❌ Wrong
    /.github/workflows/unit_testing.yml     # ✅ Correct
    
  • Spelling mistakes in the event triggers:
    on:
      pull-request:  # ❌ Wrong (hyphen)
      pullrequest:   # ❌ Wrong (no underscore)
      pull_request:  # ✅ Correct
    
  • Permission Issues: Check if your repository has have Actions enabled.
  • YAML syntax errors: Check for indentation. If the alignment in the workflow correctly, then the workflow might not run.
    on
    pull_request:  # ❌ Wrong indentation
        types: [opened]
    
    on:
      pull_request:  # ✅ Correct indentation
        types: [opened]
    

Additional setup

You can also add additional steps like caching, runner timeout, writing test results to file and more in the workflow. Here is an example.

name: Unit Tests

on:
  pull_request:
    types:
      - opened
      - synchronize

jobs:
  unitTest:
    name: Run unit tests
    runs-on: ubuntu-latest
    timeout-minutes: 10     # Timeout to avoid the runner being in a dangle state forever

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '21'
          cache: 'gradle'

      - name: Setup Gradle
        uses: gradle/gradle-build-action@v2

      # Caching
      - name: Configure Gradle properties
        run: |
          mkdir -p ~/.gradle
          echo "org.gradle.caching=true" >> ~/.gradle/gradle.properties
          echo "org.gradle.parallel=true" >> ~/.gradle/gradle.properties
          echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties          

      - name: Unit tests
        run: |
          ./gradlew :composeApp:test --no-daemon          

      # This will write the test results (pass or fail) in a html file. Helpful in viewing failed test cases
      - name: Upload Test Results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: test-results
          path: composeApp/build/reports/tests/

Example

Workflow running after raising a pull request

Directory

Workflow successful

Directory

You can refer this repository for reference. The workflow file is here.