Never miss a beat

Join my newsletter.

How to upload coverage to CodeCov for Dart and Flutter

Posted: 5/19/2022

Tagged under: dartfluttertestingcode_coverage
How to upload coverage to CodeCov for Dart and Flutter

Code Coverage helps you identify how much of your code is covered via test cases. Coverage itself is an interesting idea. People are largely divided on it but coverage can be a useful metric if used for objective information and not used to measure success. In this post, we'll cover generating coverage for flutter and dart (each individually), formatting that coverage if needed, and then uploading it to CodeCov via Github actions.

Dart and Flutter both ship with testing tools that are capable of producing coverage reports. Flutter generates a nice .lcov file for you and Dart... does not ☹️. If you're unaware, LCOV is the graphical frontend to GCOV, which is GCC's coverage testing tool. A lot of work has gone into LCOV so being able to leverage that file spec can be really beneficial. In Flutter, to get an lcov report, all we have to do is run flutter test --coverage and that will generate a file at ./coverage/ If you'd like to display that in the browser, you can use the genhtml command from lcov. Try genhtml coverage/ -o coverage/html.

Installing LCOV and Genhtml

The `genhtml` command comes from LCOV, so to use `genhtml`, you'll need to install `lcov`. You can find the install instructions for LCOV here, but if you're able to use homebrew, `brew install lcov` should get you what you need, too!

Dart, however, does not give us a nice lcov coverage file. To generate our coverage files, we can run dart run test --coverage="coverage". This will generate a coverage folder with .json files for our coverage report. These, unfortunately, aren't lcov files (which CodeCov wants), but thankfully there's a tool we can use to format our coverage as an lcov file. You can run dart pub global activate coverage to add the coverage binaries to your pub_cache path. Running $HOME/.pub-cache/bin/format_coverage --lcov --in=coverage --out=coverage.lcov --packages=.packages --report-on=lib will convert your Dart coverage files to a single lcov file.

pubcache and the path

When you install coverage, you may see something like:
Warning: Pub installs executables into $HOME/.pub-cache/bin, which is not on your path.
You can fix that by adding this to your shell's config file (.bashrc, .bash_profile, etc.):
export PATH="$PATH":"$HOME/.pub-cache/bin"

Following the above instructions can help you turn $HOME/.pub-cache/bin/format_coverage into format_coverage, which is nice but our github actions container has a small issue with the pub cache not being on the path, so we'll reference it directly through the rest of this post.

Now that we have a path to standardize our Dart and Flutter coverage reports around lcov, we can shove all of this into a github actions workflow and get things uploading. To do this, we'll use the CodeCov action (codecov/codecov-action@v2). Creating our Github workflow should be relatively easy now that we've gone through the steps up to this point. You can define whatever name and on hooks that suit your needs, but the important part for now is the jobs section.

name: Dart CI
branches: [ main ]
branches: [ main ]
runs-on: ubuntu-latest
image: dart:latest
- uses: actions/checkout@v2
- name: Install dependencies
run: dart pub get
- name: Run tests
run: dart pub run test --coverage="coverage"
- name: Install coverage tools
run: dart pub global activate coverage
- name: format coverage
run: $HOME/.pub-cache/bin/format_coverage --lcov --in=coverage --out=coverage.lcov --packages=.packages --report-on=lib
- uses: codecov/codecov-action@v2
files: coverage.lcov
verbose: true # optional (default = false)

This action will run your tests and generate a coverage report, then upload that to CodeCov. If you're using Flutter, you can swap out the Run tests step and remove the Install coverage tools and format coverage steps -- It's a lot simpler with just Flutter!

Just a heads up, Codecov works really easily with public repos, but there are a few more steps required to set up a private repo. The steps required (its mostly just generating and setting a token) can be found here: