Measuring Code Coverage for Flutter Tests

In Flutter development, ensuring code quality and reliability is crucial. One effective way to achieve this is through comprehensive testing. An essential metric for evaluating the effectiveness of tests is code coverage. Code coverage measures the extent to which your test suite exercises the codebase. In this blog post, we’ll explore how to measure code coverage for Flutter tests.

What is Code Coverage?

Code coverage is a metric that quantifies the degree to which the source code of a program has been tested. It indicates which parts of the code have been executed by the tests. Higher code coverage generally correlates with a reduced risk of undetected bugs.

Why Measure Code Coverage?

  • Identify Untested Code: Highlights parts of the codebase that lack testing.
  • Improve Test Suite: Guides developers in creating more effective tests.
  • Ensure Quality: Enhances the overall quality and reliability of the application.
  • Maintenance: Aids in understanding the impact of code changes.

Tools for Measuring Code Coverage in Flutter

Flutter provides built-in tools and third-party packages to measure code coverage effectively.

1. Flutter’s Built-in Coverage Tool

Flutter SDK includes a built-in tool that leverages the Dart SDK to collect code coverage data.

Step 1: Running Tests with Coverage

To run tests and collect coverage data, use the following command:

flutter test --coverage

This command executes all tests in the project and generates a coverage directory containing coverage data in the lcov.info format.

Step 2: Generating HTML Report

To generate an HTML report, you need to install lcov (Linux Coverage Tool). On macOS, you can install it via:

brew install lcov

On Debian-based Linux distributions:

sudo apt-get install lcov

Once lcov is installed, use the following commands to generate an HTML report:

genhtml coverage/lcov.info -o coverage/html

This will create an html directory inside the coverage directory, containing an HTML report that you can open in a browser to view coverage details.

Example Usage

flutter test --coverage
brew install lcov
genhtml coverage/lcov.info -o coverage/html
open coverage/html/index.html

2. Using the coverage Package

The coverage package offers a more sophisticated and customizable way to measure code coverage.

Step 1: Add Dependency

Add the coverage package to your dev_dependencies in pubspec.yaml:


dev_dependencies:
  flutter_test:
    sdk: flutter
  coverage: ^1.6.0

Then, run flutter pub get to install the dependencies.

Step 2: Run Tests with Coverage

Use the coverage command to collect coverage data:


dart pub global activate coverage
coverage collect_coverage --out=coverage/lcov.info --package-root=./.packages

First, ensure you activate the coverage globally to make it available.

Step 3: Generate HTML Report

Similar to the built-in tool, use lcov to generate an HTML report:


genhtml coverage/lcov.info -o coverage/html

Finally, open the report in your browser:


open coverage/html/index.html

Example: Simple Counter App and Test

Let’s consider a simple counter app and its corresponding test to demonstrate code coverage measurement.

Counter App Code

class Counter {
  int value = 0;

  void increment() {
    value++;
  }

  void decrement() {
    if (value > 0) {
      value--;
    }
  }
}
Test Code

import 'package:flutter_test/flutter_test.dart';
import 'package:your_app_name/counter.dart'; // Replace with your actual app name

void main() {
  group('Counter', () {
    test('Counter value should start at 0', () {
      final counter = Counter();
      expect(counter.value, 0);
    });

    test('Counter value should be incremented', () {
      final counter = Counter();
      counter.increment();
      expect(counter.value, 1);
    });

    test('Counter value should be decremented if greater than 0', () {
      final counter = Counter();
      counter.increment();
      counter.decrement();
      expect(counter.value, 0);
    });

    test('Counter value should not be decremented if 0', () {
      final counter = Counter();
      counter.decrement();
      expect(counter.value, 0);
    });
  });
}

Replace your_app_name with your actual app name and ensure that the paths are correctly set up. Now run the tests and generate the code coverage report using the methods described above.

Analyzing the Coverage Report

The HTML report will display which lines of code are covered by the tests. You can click on files to see line-by-line coverage details. The report will typically show lines marked as:

  • Covered: Executed by tests.
  • Not Covered: Not executed by any test.
  • Partially Covered: Only partially executed by tests (e.g., only one branch of an if statement).

Focus on improving coverage for uncovered or partially covered lines by adding or modifying tests.

Best Practices for Code Coverage

  • Aim for High Coverage: While 100% coverage isn’t always necessary, aim for a high percentage to ensure critical paths are well-tested.
  • Write Meaningful Tests: Focus on writing tests that validate behavior rather than just lines of code.
  • Test Edge Cases: Cover boundary conditions, error conditions, and unexpected inputs.
  • Review Coverage Regularly: Incorporate coverage measurement into your CI/CD pipeline to track coverage changes over time.
  • Integrate with CI/CD: Automate the coverage measurement process using Continuous Integration tools like GitHub Actions or Jenkins to enforce coverage thresholds.

Example GitHub Actions workflow:


name: Flutter CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: '17'

      - uses: subosito/flutter-action@v2
        with:
          channel: 'stable'

      - run: flutter pub get
      - run: flutter test --coverage
      - run: |
          sudo apt-get install lcov
          genhtml coverage/lcov.info -o coverage/html
      - uses: actions/upload-artifact@v3
        with:
          name: code-coverage-report
          path: coverage/html

Conclusion

Measuring code coverage is a vital practice in Flutter development for ensuring code quality and reliability. By utilizing Flutter’s built-in tools or the coverage package, developers can identify untested areas, improve test suites, and maintain high-quality applications. Regularly analyzing coverage reports and incorporating coverage measurement into CI/CD pipelines will lead to more robust and dependable software. Make code coverage an integral part of your Flutter development workflow to deliver exceptional applications.