The Integration Test Helper has pre-configured methods that allow for faster test deployment for end to end (e2e) test coverage.

Overview

The Integration Test Helper has pre-configured methods that allow for faster test deployment for end to end (e2e) test coverage (using Android and iOS platform UIs).

Open Drawer

Languages

Counter

The MAC

All Pages

Features

The Integration Test Helper is built on top of Flutter's Integration Tests. Running End to End (e2e) tests can become bloated and unorganized code, and lead to regressions but with this helper, writing tests can be faster, modular and with full test coverage. This approach allows for a cleaner development experience, and less regressions within your apps.

Regression Testing

Integration Test Helper (or the BaseIntegrationTest class) allows for BlackBox Testing using fixture data. The fixtures currently support JSON data, and can be loaded from anywhere within the project folder. Here is what the fixture test data (assets/fixtures/languages.json) looks like that is being blackbox tested...

{
    "count": 7,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": 1,
            "name": "Python",
            "year": 1991,
            "person": "Guido van Rossum",
            "favorited": true,
            "category" : "Scripting, Object Oriented",
            "logo": "logos/python.png",
            "hello" : "helloworld/1_code_prism_language_python.png",
            "arguments" : "arguments/1_code_prism_language_python.png",
            "description" : "Python is an interpreted high-level general-purpose programming language. Guido van Rossum began working on Python in the late 1980s, as a successor to the ABC programming language, and first released it in 1991 as Python 0.9.0. Python’s design philosophy emphasizes code readability with its notable use of significant indentation. Its language constructs as well as its object-oriented approach aim to help programmers write clear, logical code for small and large-scale projects."
        },
        ...
    ]
}

This data is typically initialized in the setupInitialData implementation of the BaseIntegrationTest subclass. The following is an example of how you can BlackBox Test your ListViews, as well other types of Widgets with Integration Test Helper:

class ScreenIntegrationTestGroups extends BaseIntegrationTest {

    late Map _languagesTestData;

    @override
    Future<void> setupInitialData() async {

        _languagesTestData = await loadFixtureJSON('assets/fixtures/languages.json') as Map;

        if (_languagesTestData.isEmpty) {
            throw 'No languages test data found';
        }

    }

    Future<void> validateTestDataAt(int itemIndex, { required String widgetSuffix, required String jsonKey }) async {
        var languageData = _languagesTestData['results'][itemIndex] as Map;
        var itemText = languageData[jsonKey] as String;
        await verifyListExactText(itemIndex, widgetPrefix: 'item', widgetSuffix: widgetSuffix, expectedText: itemText);
    }
        
    Future<void> testLanguagesFeature() async {
        
        // VIEW LANGUAGES PAGE
        await showLanguagesList();
        await verifyTextForKey('app-bar-text', 'Languages');

        await validateTestDataAt(0, widgetSuffix: 'name', jsonKey: 'name');
        await validateTestDataAt(1, widgetSuffix: 'name', jsonKey: 'name');

        // VIEW LANGUAGE Python PAGE
        await tapListItem(widgetPrefix: 'item', itemIndex: 0);
        await verifyExactText('Python');
        await tapBackArrow();

        // VIEW LANGUAGE Java PAGE
        await tapListItem(widgetPrefix: 'item', itemIndex: 1);
        await verifyExactText('Java');
        await tapBackArrow();

    }

    Future<void> testCounterFeature() async {

        await showCounterSample();
        await verifyTextForKey('app-bar-text', 'Counter Sample');
        ...

    }

    ...
    
}

Integration Test Helper also supports all Major Widget Interactions. When tapping Widgets, the package supports tapForKey, tapForType, tapForTooltip, tapWidget("Containing This Text"), tapListItem and more.

With the tapListItem, we handle the waiting for the UI to load, finding the Widget, and then tapping the found Widget. In addition, we also include ListView item prefixes, and positions within the list.

    
    Future<void> tapListItem({ required String widgetPrefix, required int itemIndex }) async {
        await waitForUI();
        final itemFinder = find.byKey(ValueKey('${widgetPrefix}_$itemIndex'));
        await tester.tap(itemFinder);
    }

Note: Using the tapListItem implementation, we remove at the least 3 lines of code from your integration tests, and allow that functionality to be reused in your own custom implementation of the BaseIntegrationTest class.

Here is what your Widget Key implementation could look like:

    Card(
        elevation: 1.5,
        child: InkWell(
            key: Key('item_$index'),
            onTap: () {
                Navigator.push<void>(context,
                    MaterialPageRoute(builder: (BuildContext context) =>
                            LanguagePage(index: index, language: item)));
            },
            child: LanguagePreview(index: index, language: item)),
        ),
    );

And here is an example of using that Key to tap the list item widget:

        
    Future<void> testLanguagesFeature() async {
        
        // VIEW LANGUAGES PAGE
        ...

        // VIEW LANGUAGE Python PAGE
        await tapListItem(widgetPrefix: 'item', itemIndex: 0);
        await verifyExactText('Python');
        await tapBackArrow();

        // VIEW LANGUAGE Java PAGE
        ...

    }

Getting started

Note: this package example uses another one of our packages. It's called the drawer_manager package, and can be found here for more details on how it works.

Install Provider, Drawer Manager & Integration Test Helper

  flutter pub get provider
  flutter pub get drawer_manager
  flutter pub get integration_test_helper

Or install Provider, Drawer Manager & Integration Test Helper (in pubspec.yaml)

    ...
    
dependencies:
  flutter:
    sdk: flutter

    ...

  provider: 6.0.2
  drawer_manager: 0.0.4
    
dev_dependencies:

  flutter_test:
    sdk: flutter

  integration_test:
    sdk: flutter

  integration_test_helper: <latest_version>

Add Integration Test Driver file (test_driver/app_features_test.dart)

import 'package:integration_test/integration_test_driver.dart';

Future<void> main() => integrationDriver();

Usage

Create hello file (lib/hello.dart)

import 'package:flutter/material.dart';

class HelloPage extends StatelessWidget {

  final int position;
  
  const HelloPage({Key? key, required this.position}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(
        'Hello, Flutter $position!',
        key: Key('hello-page-text-$position'),
        textAlign: TextAlign.center,
        style: const TextStyle(
            color: Color(0xff0085E0),
            fontSize: 48,
            fontWeight: FontWeight.bold
        )
      ),
    );
  }
}

Create main file (lib/main.dart)

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:drawer_manager/drawer_manager.dart';

import 'hello.dart';

void main() {
  runApp(setupMainWidget());
}

Widget setupMainWidget() {
  WidgetsFlutterBinding.ensureInitialized();
  return const MyApp();
}

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

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<DrawerManagerProvider>(
        create: (_) => DrawerManagerProvider(),
        child: MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(primarySwatch: Colors.blue),
          home: const MyHomePage(),
        ));
  }
}

class MyHomePage extends StatelessWidget {

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

  String _getTitle(int index) {
      switch (index) {
        case 0: return 'Hello 1';
        case 1: return 'Hello 2';
        default: return '';
      }
  }

  Widget _getTitleWidget() {
    return Consumer<DrawerManagerProvider>(builder: (context, dmObj, _) {
      return Text(
        _getTitle(dmObj.selection),
        key: const Key('app-bar-text')
      );
    });
  }

  @override
  Widget build(context) {

    final drawerSelections = [
      const HelloPage(position: 1),
      const HelloPage(position: 2),
    ];
    
    final manager = Provider.of<DrawerManagerProvider>(context, listen: false);

    return Scaffold(
        appBar: AppBar(title: _getTitleWidget()),
        body: manager.body,
        drawer: DrawerManager(
          context,
          drawerElements: [
            const DrawerHeader(
              decoration: BoxDecoration(color: Colors.blue),
              child: Padding(
                padding: EdgeInsets.only(bottom: 20),
                child: Icon(
                  Icons.account_circle,
                  color: Colors.blueGrey,
                  size: 96,
                ),
              ),
            ),
            DrawerTile(
              key: const Key('drawer-hello-1'),
              context: context,
              leading: const Icon(Icons.hail_rounded),
              title: Text(_getTitle(0)),
              onTap: () async {
                // RUN A BACKEND Hello, Flutter OPERATION
              },
            ),
            DrawerTile(
              key: const Key('drawer-hello-2'),
              context: context,
              leading: const Icon(Icons.hail_rounded),
              title: Text(_getTitle(1)),
              onTap: () async {
                // RUN A BACKEND Hello, Flutter OPERATION
              },
            )
          ],
          tileSelections: drawerSelections,
        ));
    }

}

Import Flutter Test & Integration Test Helper (in integration_test/app_test_groups.dart)

    ...
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test_helper/integration_test_helper.dart';

Subclass BaseIntegrationTest (in integration_test/app_feature_groups.dart)

The Integration Test Helper can support platform specific implementations, like the showHelloFlutter method. This method uses the Drawer for Android and accomodates the Android environment.

class ScreenIntegrationTestGroups extends BaseIntegrationTest {

    // ...

    @override
    Future<bool> isPlatformAndroid() async {
        return Future.value(true);
    }

    @override
    Future<void> setupInitialData() async {
        // ...
    }

    Future<void> showHelloFlutter({required int position}) async {
        print('Showing Hello, Flutter $position!');
        if(Platform.isAndroid) {
            await tapForTooltip('Open navigation menu');
            await tapForKey('drawer-hello-$position');
        }
        await waitForUI();
    }

    Future<void> testHelloFlutterFeature() async {
        await showHelloFlutter(position: 1);
        await verifyTextForKey('app-bar-text', 'Hello 1');
        await verifyTextForKey('hello-page-text-1', 'Hello, Flutter 1!');

        await showHelloFlutter(position: 2);
        await verifyTextForKey('app-bar-text', 'Hello 2');
        await verifyTextForKey('hello-page-text-2', 'Hello, Flutter 2!');
    }

    // ...

}

Setup BaseIntegrationTest Subclass (in integration_test/app_features.dart)

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

import 'package:example/main.dart' as app;
import 'app_feature_groups.dart';

void main() async {

    IntegrationTestWidgetsFlutterBinding.ensureInitialized();

    testWidgets('Testing end to end single-screen integration', (WidgetTester tester) async {
      
          final main = app.setupMainWidget();
          final integrationTestGroups = ScreenIntegrationTestGroups();
          await integrationTestGroups.initializeTests(tester, main);

          await integrationTestGroups.testHelloFlutterFeature();

      }, timeout: const Timeout(Duration(minutes: 1))
    );
    
}

Run Driver on BaseIntegrationTest Subclass (using integration_test/app_features.dart)

    flutter drive -t integration_test/app_features.dart

Additional information

Alternatively, you can run the example

The example project has 5 screens that have grouped integration tests:

Package Support

To support this repo, take a look at the SUPPORT.md file.

Package Documentation

To view the documentation on the package, follow this link

You might also like...

A server app built using Shelf, configured to enable running with Docker

A server app built using Shelf, configured to enable running with Docker

A server app built using Shelf, configured to enable running with Docker. Project Structure Running the sample Running with the Dart SDK You can run t

Jan 12, 2022

Buildpack dart - A server app built using Shelf, configured to enable running with Docker

Hosted at https://dart-buildpack-demo-ruyjilv5wq-uc.a.run.app/ Buildpack defined

Dec 15, 2022

A server app built using Shelf, configured to enable running with Docker.

A server app built using Shelf, configured to enable running with Docker.

May 23, 2022

A dart timer that can be configured to fire once or repeatedly with ability start, stop, resume and cancel.

A timer that can be configured to fire once or repeatedly with ability start, stop, resume and cancel. Getting started Add CompleteTimer to your pubsp

Jul 20, 2022

A package of pre-built `TextInputFormatter` objects

text_formatters A package of pre-built TextInputFormatter objects to use with Flutter's TextField or TextFormField widgets. Formatters UppercaseInputF

Jul 24, 2020

Pre-defined setup to start developing flutter project with stacked architecture.

un_official A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if t

Dec 27, 2021

This is a template repository for starting flutter apps with some pre build codes.

flutter_mobile_template A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you

Nov 16, 2022

A customizable carousel slider for Flutter. Supports infinite sliding, custom indicators, and custom animations with many pre-built indicators and animations.

A customizable carousel slider for Flutter. Supports infinite sliding, custom indicators, and custom animations with many pre-built indicators and animations.

Flutter Carousel Slider A customizable carousel slider for flutter Screenshots Installing dependencies: flutter_carousel_slider: ^1.0.8 Demo Demo a

Nov 6, 2022

An rx stream builder widget that is able to pre-populate a flutter StreamBuilder with data from an rx stream if the stream is either a value or a replay observable.

An rx stream builder widget that is able to pre-populate a flutter StreamBuilder with data from an rx stream if the stream is either a value or a replay observable. For example the RX stream is a BehaviorSubject or a ReplaySubject.

Jan 22, 2022
Comments
  • Package validation found the following error

    Package validation found the following error

    Package validation found the following error:

    • line 8, column 1 of lib/integration_test_helper.dart: flutter_test is in the dev_dependencies section of pubspec.yaml. Packages used in lib/ must be declared in the dependencies section.
        ╷
      8 │ import 'package:flutter_test/flutter_test.dart';
        │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        ╵
    

    Sorry, your package is missing a requirement and can't be published yet. For more information, see: https://dart.dev/tools/pub/cmd/pub-lish.

    pub finished with exit code 65
    
    opened by cdm2012 0
  • sample is not work

    sample is not work

    VMServiceFlutterDriver: Connecting to Flutter application at http://127.0.0.1:56932/p1SKnDpyL2g=/ VMServiceFlutterDriver: Isolate found with number: 2750662995495631 VMServiceFlutterDriver: Isolate is paused at start. VMServiceFlutterDriver: Attempting to resume isolate E/flutter (26931): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Binding has not yet been initialized. E/flutter (26931): The "instance" getter on the WidgetsBinding binding mixin is only available once that binding has been initialized. E/flutter (26931): Typically, this is done by calling "WidgetsFlutterBinding.ensureInitialized()" or "runApp()" (the latter calls the former). Typically this call is done in the "void main()" method. The "ensureInitialized" method is idempotent; calling it multiple times is not harmful. After calling that method, the "instance" getter will return the binding. E/flutter (26931): In a test, one can call "TestWidgetsFlutterBinding.ensureInitialized()" as the first line in the test's "main()" method to initialize the binding. E/flutter (26931): If WidgetsBinding is a custom binding mixin, there must also be a custom binding class, like WidgetsFlutterBinding, but that mixes in the selected binding, and that is the class that must be constructed before using the "instance" getter. E/flutter (26931): #0 BindingBase.checkInstance. (package:flutter/src/foundation/binding.dart:284:9) E/flutter (26931): #1 BindingBase.checkInstance (package:flutter/src/foundation/binding.dart:366:6) E/flutter (26931): #2 WidgetsBinding.instance (package:flutter/src/widgets/binding.dart:273:53) E/flutter (26931): #3 IntegrationTestHelperBinding.ensureInitialized (package:integration_test_helper/integration_test_helper.dart:21:24) E/flutter (26931): #4 main (file:///Users/xiaoyezi/AndroidStudioProjects/integration_test_helper/example/integration_test/app_features.dart:15:48) E/flutter (26931): #5 _runMain. (dart:ui/hooks.dart:134:23) E/flutter (26931): #6 _delayEntrypointInvocation. (dart:isolate-patch/isolate_patch.dart:297:19) E/flutter (26931): #7 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12) E/flutter (26931): I/example.exampl(26931): ProcessProfilingInfo new_methods=984 is saved saved_to_disk=1 resolve_classes_delay=8000 VMServiceFlutterDriver: Flutter Driver extension is taking a long time to become available. Ensure your test app (often "lib/main.dart") imports "package:flutter_driver/driver_extension.dart" and calls enableFlutterDriverExtension() as the first call in main().

    When I run the sample, it prompts an error like this, I try to run it, but it still fails

    opened by zhangyc 0
  • 'CupertinoTextField' is not a subtype of type 'TextField'

    'CupertinoTextField' is not a subtype of type 'TextField'

    The internal functionality of the BaseIntegrationTest.enterText defaults to EditText. I'm currently using a CupertinoEditText and it is failing with the following message/stack.

    I/flutter (11696): The following _CastError was thrown running a test:
    I/flutter (11696): type 'CupertinoTextField' is not a subtype of type 'TextField' in type cast
    I/flutter (11696): 
    I/flutter (11696): When the exception was thrown, this was the stack:
    I/flutter (11696): #0      BaseIntegrationTest.enterText (package:integration_test_helper/integration_test_helper.dart:135:56)
    I/flutter (11696): <asynchronous suspension>
    I/flutter (11696): #1      ScreenIntegrationTestGroups.testConcreteNumberFeature(app_feature_groups.dart:55:9)
    I/flutter (11696): <asynchronous suspension>
    I/flutter (11696): #2      ScreenIntegrationTestGroups.testEndToEndUsing (app_feature_groups.dart:37:9)
    I/flutter (11696): <asynchronous suspension>
    I/flutter (11696): #3      main.<anonymous closure> (app_features.dart:35:11)
    I/flutter (11696): <asynchronous suspension>
    I/flutter (11696): #4      testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:170:15)
    I/flutter (11696): <asynchronous suspension>
    I/flutter (11696): #5      TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:806:5)
    I/flutter (11696): <asynchronous suspension>
    
    
    
    opened by cdm2012 0
Owner
The Mobile Applications Community
The Mobile Applications Community is where apps can be built using community resources.
The Mobile Applications Community
Appwrite is a secure end-to-end backend server for Web, Mobile, and Flutter developers that is packaged as a set of Docker containers for easy deployment 🚀

A complete backend solution for your [Flutter / Vue / Angular / React / iOS / Android / *ANY OTHER*] app Appwrite 0.12 has been released! Learn what's

Appwrite 28.2k Jan 3, 2023
Integration test - Copy of the official Flutter integration test plugin

integration_test This package enables self-driving testing of Flutter code on de

null 0 Jan 5, 2022
A dart package for many helper methods fitting common situations

Basic Utils A dart package for many helper methods fitting different situations. Table of Contents Basic Utils Table of Contents Preamble Install pubs

null 275 Jan 5, 2023
A package script for allowing coverage test tool to see all Dart files

full_coverage Coverage tools like codecov only see the files that were actually triggered by tests. This means that a coverage of 100% can easily be a

Flutterando 2 Mar 18, 2022
A CLI tool to verify the test coverage of a pull request only, ignoring the rest of the project

This is a CI tool that analyzes the coverage rate of a pull request, ignoring the lines that were not changed in the PR. Motivation The coverage rate

Tales Barreto 3 Dec 15, 2022
PrivateFit is an E2E encrypted fitness application built on the atPlatform.

Private Fit Now for a little internet optimism Generated by the at_app CLI and Very Good CLI ?? A secure fitness app. Getting Started ?? This project

The Atsign Foundation 6 Oct 31, 2022
Getx and Dio APi-Integration - Flutter RestApi Integration using Dio

Flutter RestApi Integration using Dio. Click this image to find videos==> //Crud

Fsd Ramjan 9 Nov 5, 2022
Flutter-fb-integration - Flutter And firebase integration Guide

Quickstart Guide This project still use my firebase server config, if you want t

Naufal Aldy Pradana 0 Feb 2, 2022
FLutter Api Integration - Flutter Rest API Integration

Flutter_Rest_Api_integration Flutter_Rest_Api_integration. Preview How To Use To

Rahul Ranjan Singh 0 Feb 17, 2022
A server app built using Shelf, configured to enable running with Docker

A server app built using Shelf, configured to enable running with Docker. This sample code handles HTTP GET requests to / and /echo/<message> Requests

Tornike Gogberashvili 1 Jan 26, 2022