This tutorial gives a introduction into testing Flutter mobile applications.

1. Testing with Flutter

Flutter supports:

  • Unit tests

  • Widget tests

  • Integration tests

1.1. Unit Tests

Unit tests only test the behavior of a single function. They are very fast and lightweight in the execution, but might not test the whole app and thus lead to not discovered bugs.

As the app will not be running most of the required dependencies need to be mocked.

To get started add the test package as a dependency to the pubspec.yaml:

devDependencies:
  test: ^1.9.4

Tests are located in the test/ directory at the root of your app’s directory. In this folder each class (containing test cases) should correspond to an actual file inside your apps lib/ directory. It is also good practice to follow the same structure.

For example:

counter_app/
  lib/
    counter.dart
  test/
    counter_test.dart

Test cases comprise of the call of test(name, fn). The first parameter is the name (or a short description) of the test. The second parameter is a function that is executed and contains the actual logic being tested.

To group multiple test cases the group(name, fn) function can be used. Inside its second parameter you can call test(…​) multiple times.

An example test class could look like this:

import 'package:test/test.dart'; (1)
import 'package:counter_app/counter.dart'; (2)

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

    test('value should be incremented', () {
      final counter = Counter();

      counter.increment();

      expect(counter.value, 1);
    });

    test('value should be decremented', () {
      final counter = Counter();

      counter.decrement();

      expect(counter.value, -1);
    });
  });
}

1.2. App testing

1.2.1. Overview

The WidgetTester utility provides an easy way to interact with your application in your tests. For example, you can send tap and scroll gestures. You can also use WidgetTester to:

  • find child widgets in the widget tree

  • read text

  • verify that the values of widget properties are correct

The following methods allow you to wait for your application:

  • pump() - Triggers a rebuild of the widgets

  • pumpAndSettle() - Repeatedly calls pump with the given duration until there are no longer any frames scheduled, e.g. all animations have ended

1.2.2. Finding widgets

You can search for widgets via a specific key, the widget instance or via a displayed text in the widget.

// Search for text H and validate that only one widget is found
expect(find.text('H'), findsOneWidget);

// Find by key which has been assigned to the widget
final testKey = Key('K');
expect(find.byKey(testKey), findsOneWidget);

// Find a specific widget
final childWidget = Padding(padding: EdgeInsets.zero);
expect(find.byWidget(childWidget), findsOneWidget);

1.2.3. Example

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import 'package:flutter_stackoverflow/main.dart';

void main() {
  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(MyApp());

    // Verify that our counter starts at 0.
    expect(find.text('0'), findsOneWidget);
    expect(find.text('1'), findsNothing);

    // Tap the '+' icon and trigger a frame.
    await tester.tap(find.byIcon(Icons.add));
    await tester.pump();

    // Verify that our counter has incremented.
    expect(find.text('0'), findsNothing);
    expect(find.text('1'), findsOneWidget);
  });
}

1.3. Running tests

Use flutter test to run your tests via the command line.

When you select Debug → Start Debugging in a Flutter project and the the active file is in the test folder, then it will be launched via flutter test. Is requires that there is no program specified in your launch.json (or you don’t have one).

2. Exercise: Testing with Flutter

3. Links and Literature