Testing Dart packages with GitHub Actions

The Problem

As the author of a several Flutter applications & Dart packages, I have spent entirely too much of my life scouring the internet and piecing together tutorials for CI/CD workflows for Flutter applications & Dart packages, so I decided to publish my own. The following article is meant to demonstrate a simple workflow for a Flutter/Dart package using GitHub Actions, my preferred platform for CI/CD. You can see a fully working example of the workflow here.

Create a Code Coverage account

To publish code coverage results, you will want to create a code coverage account. You can simplify the process by authenticating with GitHub. This example uses codecov.io but coveralls should have a similar process.

1. Add a new repository

From the Repositories tab, select "Add a new repository".

add_repository.png

2. Find your repository

You will be taken to a page that allows you to search for the repository that you want to add coverage support to. Find your repository and select it.

choose_repository.png

3. Save your upload token

On the next screen, you'll be presented with an Upload token. Save this - it will be added as a secret to your project to upload code coverage results.

Configure Code Coverage

Next you'll need to set up the GitHub integration with Code Coverage. The easiest way to do this is to select the GitHub Integration action from the Account settings page.

configure_coverage.png

From there you'll be redirected to add the integration in GitHub. You can set up code coverage to be configured for all repositories or only select. I set mine up for specific repositories. We'll also want to add that code coverage token from the previous step as a new secret. Call the secret CODECOV_TOKEN.

create_secret.png

Setting up the GitHub Action

We could create a GitHub Action from our IDE of choice or directly from GitHub. For the sake of brevity, let's use GitHub.

1. Create an action

create_action.png

From the Actions tab of your GitHub repository, select "set up a workflow yourself". You could use the Dart workflow to get a simple workflow set up for you, but this workflow will only work if you do not need Flutter as a dependency. You can see an example that just uses the Dart workflow here.

2. Name the workflow.

name: Test

You can name the workflow whatever you want. Because this workflow is only testing the package, I've given it the name test.

3. Define workflow triggers.

on: 
  push: 
     branches: [ master ] 
  pull_request: 
     branches: [ master ]

Triggers determine when a GitHub Action runs. You can learn more about them here. For our workflow, we probably want to run tests for each pull request to know whether something broke before we merge. We also want to run tests on pushes directly to master in case an administrator accidentally (or intentionally in my case) pushes a bad commit without going through the PR process.

4. Create the job to test

Now it's time to define the meat of our workflow file. Let's examine each step individually.

a. Specify the running platform

jobs: 
  test: 
    runs-on: macos-latest

 steps:

You can specify any number of platforms to run your jobs on. In this case we use macOS, but this is only really necessary if you're building iOS/macOS apps. From here, we will define the steps that will be ran as part of the workflow. Think of each step as a command to be executed.

b. Add step to checkout project

 - uses: actions/checkout@v1

The first step is to check out the project so that we may run additional commands against it.

c. Add step to setup Flutter

 - name: Install Flutter 
   uses: subosito/flutter-action@v1.3.2

Next, we install Flutter so that we may execute Flutter commands in later steps.

d. Add step to install dependencies

 - name: Install dependencies 
   run: flutter pub get

Next, we install the project dependencies.

e. Add step to run tests with coverage

 - name: Test app 
   run: flutter test --coverage

This workflow would be nothing without our command to run our tests, so let's add that. Notice that to generate coverage when running tests, we need to use the --coverage flag. By default the code coverage will be output to coverage/lcov.info.

f. Add step to publish coverage results

 - name: Upload coverage to Codecov 
   uses: codecov/codecov-action@v1 
   with: 
   token: ${{ secrets.CODECOV_TOKEN }} 
   file: coverage/lcov.info

Finally, we publish our code coverage results using the previously defined token. The token is made available as an environment variable on the secrets object. We also need to specify the location of our code coverage results.

NOTE: Adding the token is optional, it is automatically grabbed by the codecov action, but for the sake of this article I am being explicit.

Complete workflow file

We have finished building our workflow file. It should closely resemble the following:

name: Test

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  test:
    runs-on: macos-latest

    steps:
    - uses: actions/checkout@v1
    - name: Install Flutter
      uses: subosito/flutter-action@v1.3.2
    - name: Install app dependencies
      run: flutter pub get
    - name: Test app
      run: flutter test --coverage
    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v1
      with:
        token: ${{ secrets.CODECOV_TOKEN }}
        file: coverage/lcov.info

This step allows us to publish our code coverage results using the previously defined token. The token is made available as an environment variable on the secrets object. We also need to specify the location of our code coverage results.

5. Commit your changes, have a tea break

All that's left is to commit these changes and we're done! You should immediately be able to see your action running.

action_results.png

Conclusion

Hopefully you now have a better understanding of how GitHub Actions work and how to can set up a simple workflow for your Flutter application or package without involving any pesky 3rd party services. Happy trails on your DevOps journey. šŸ˜ŠšŸ˜ŠšŸ˜Š

Sup?! Iā€™m Ryan Edge. I am a Software Engineer at Superformula and a semi-pro open source contributor. If you liked this article, be sure to follow and spread the love! Happy trails

No Comments Yet