How to run integration tests on Flutter apps
Tags: flutter, testing
Published: 2022-03-07 05:26:21

Flutter, designed by Google, is a free and open-source framework for building visually appealing, natively compiled, and multi-platform applications from a single codebase.

Flutter has become quite popular due to its ability to create multi-platform apps. It enables users to build, deploy and test web, mobile, desktop, and embedded applications from a single codebase.

In this article, let’s explore how to run integration tests on Flutter apps.

## Table of Contents

1. Introduction To Flutter 
2. Features of Flutter Framework
3. Testing In Flutter Apps
4. Integration Testing In Flutter Apps
5. Migrating From Flutter Driver
6. Integration_Test Package
7. Steps To Perform Integration Testing In Flutter Apps
8. Integration Tests In Flutter Apps With BrowserStack 

## Introduction To Flutter 
Flutter is an open-source mobile UI framework that can create Android and iOS apps (among other platforms) from a single codebase.

Generally, frameworks offer different features to develop mobile applications. For example, Android provides a framework based on Java and Kotlin to develop mobile apps, whereas iOS uses a framework based on Objective-C or Swift language.

As a result, Devs had to use two different languages and frameworks to develop applications for both OS. Naturally, a cross-platform framework like Flutter makes life simpler for them by saving resources, time, and effort.

## Features of Flutter Framework

1. Facilitates fast development
2. Multi-Platform support
3. Provides a modern and reactive framework
4. Uses the easy to learn Dart programming language
5. Fluid and intuitive user interfaces
6. Vast widget catalog
7. Runs the same UI for multiple platforms
8. Stable and Reliable

## Testing In Flutter Apps
Flutter supports three types of tests:

1. Unit Test
2. Widget Test
3. Integration Test

- A unit test verifies the behavior of a method or class.
- A widget test checks the behavior of Flutter widgets without having to run the app.
- An integration test (also called end-to-end testing or GUI testing) runs the entire application.

## Integration Testing In Flutter Apps
Unit tests and Widget tests can test individual classes, widgets, or functions. However, they don’t verify how individual components work together or monitor application performance on real devices. This is where integration tests come in. 

Integration tests in Flutter are written using the integration test package, provided by the SDK. This is Flutter’s version of Selenium WebDriver (generic web), Protractor (Angular), Espresso (Android), or Earl Gray (iOS). The package internally uses flutter driver to drive the test on a device.

Tests written with the integration_test package can:

1. Run directly on the target device, enabling tests on numerous Android or iOS devices.
2. Run using flutter test integration_test.
3. Use flutter_test APIs, which makes the creation of integration tests similar to creating widget tests.

## Migrating From Flutter Driver

When Flutter was introduced, integration tests were written using flutter_driver, which enabled testers to maintain programmatic control of a Flutter app. On running the futter drive command, it initiates 2 processes:

- A “driver” process on the host machine (i.e. the developer’s laptop) that sends instructions to and receives data from the app.
- The app is configured to identify connections coming from the driver process.
This allowed the developer to write tests in plain Dart that could interact with the app. However, it came with significant disadvantages:

- The driver code couldn’t share any code with the app or unit tests. In fact, it couldn’t leverage any package: flutter dependencies.
- It relied on strongly-typed APIs because it could not import Key, Widget, or MyCustomWidget. find.byType(‘EvnetsPage’) was often mistyped and misread.

## Integration_Test Package
The integration_test package was released to fix some of the issues mentioned above. Here, the code for tests runs in the same isolate as the app code itself. In other words, it can access the same memory. This essentially resolves the previous issues, and offers a few other benefits:

1. Same API as component tests.
2. Can share code with the app.
3. Internal app state is totally visible to tests, and runApp is called inside the test.
4. Since tests are built into the app executable, they can now run on physical devices.

## Steps To Perform Integration Testing In Flutter Apps
This example will demonstrate how to test a counter app. This demonstrates how to set up integration tests, how to verify if a specific text is being displayed by the app, how to tap specific widgets, and how to run integration tests.

In order to perform the Integration test, follow the below steps:

1. Create an app to test.
2. Add the integration_test dependency.
3. Create the test files.
4. Write the integration test.
5. Run the integration test.

## Create an app to test

First, create an app for testing. This test will use a counter app produced by the flutter create command. This app will let a user tap on a button to increase one count.

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

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {

  const MyApp({Key? key}) : super(key: key);

  @override

  Widget build(BuildContext context) {

    return const MaterialApp(

      title: 'Counter App',

      home: MyHomePage(title: 'Counter App Home Page'),

    );

  }

}

class MyHomePage extends StatefulWidget {

  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override

  _MyHomePageState createState() => _MyHomePageState();

}

class _MyHomePageState extends State<MyHomePage> {

  int _counter = 0;

  void _incrementCounter() {

    setState(() {

      _counter++;

    });

  }

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: Text(widget.title),

      ),

      body: Center(

        child: Column(

          mainAxisAlignment: MainAxisAlignment.center,

          children: <Widget>[

            const Text(

              'You have pushed the button this many times:',

            ),

            Text(

              '$_counter',

              // Provide a Key to this specific Text widget. This allows

              // identifying the widget from inside the test suite,

              // and reading the text.

              key: const Key('counter'),

              style: Theme.of(context).textTheme.headline4,

            ),

          ],

        ),

      ),

      floatingActionButton: FloatingActionButton(

        // Provide a Key to this button. This allows finding this

        // specific button inside the test suite, and tapping it.

        key: const Key('increment'),

        onPressed: _incrementCounter,

        tooltip: 'Increment',

        child: const Icon(Icons.add),

      ),

    );

  }

}
```
[Source](https://flutter.cn/docs/cookbook/testing/integration/introduction)

## Add the integration_test dependency
Now, use the **integration_test**, **flutter_driver**, and **flutter_test **packages for writing integration tests.

Add these dependencies to the dev_dependencies section of the app’s pubspec.yaml file. The location of the package should be the Flutter SDK.

```
# pubspec.yaml

dev_dependencies:

  integration_test:

    sdk: flutter

  flutter_test:

    sdk: flutter
```

 

## Create the test files
Create a new directory, integration_test, with an empty app_test.dart file:

```
counter_app/

  lib/

    main.dart

  integration_test/

    app_test.dart
```
[Source](https://docs.flutter.dev/cookbook/testing/integration/introduction)

## Write the integration test
Now, write the integration test with the steps below:

1. Initialize IntegrationTestWidgetsFlutterBinding, a singleton service that runs tests on a physical device.
2. Interact and test widgets via the WidgetTester class.
3. Test the necessary scenarios.

```
import 'package:flutter_test/flutter_test.dart';

import 'package:integration_test/integration_test.dart';

import 'package:introduction/main.dart' as app;

void main() {

  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  group('end-to-end test', () {

    testWidgets('tap on the floating action button, verify counter',

        (WidgetTester tester) async {

      app.main();

      await tester.pumpAndSettle();

      // Verifies that the counter starts at 0.

      expect(find.text('0'), findsOneWidget);

      // Finds the floating action button to tap on.

      final Finder fab = find.byTooltip('Increment');

      // Emulates a tap on the floating action button.

      await tester.tap(fab);

      // Triggers a frame.

      await tester.pumpAndSettle();

      // Verifies if the counter increments by 1.

      expect(find.text('1'), findsOneWidget);

    });

  });

}
```
[Source](https://docs.flutter.dev/cookbook/testing/integration/introduction)

## Run the integration test
The process of executing integration tests depends greatly on the platform used. If the platform so permits, You can test on mobile devices or web browsers.

1. Mobile

To test on a real iOS / Android device, first, connect the device and run the following command from the root of the project:

```
flutter test integration_test/app_test.dart
```
Or, specify the directory to run all integration tests:

```
flutter test integration_test
```
This command runs the app and integration tests on the target device.

2. Web

To test on a web browser (Chrome, in this example), [Download ChromeDriver](https://chromedriver.chromium.org/downloads).

Next, create a new directory named test_driver containing a new file named integration_test.dart:

```
import 'package:integration_test/integration_test_driver.dart';

Future<void> main() => integrationDriver();
```
Launch WebDriver, for example:

```
chromedriver --port=4444
```
From the root of the project, run the following command:

```
flutter drive \

  --driver=test_driver/integration_test.dart \

  --target=integration_test/app_test.dart \

  -d web-server
```
[Source](https://docs.flutter.dev/cookbook/testing/integration/introduction)