LogoPatrol

Native automation setup

Using Patrol's native automation feature requires more setup work than using custom finders. Unfortunately it's necessary, because you have to integrate your app with Android and/or iOS native testing tools.

We believe that the powerful features you'll get will make up for it!

Add dependency on patrol#

If you haven't already, add a dependency on the patrol package in the dev_dependencies section of pubspec.yaml.

flutter pub add patrol --dev

Configure Patrol in pubspec.yaml#

This step is not necessary, but it's recommended.

Create patrol section in your pubspec.yaml:

pubspec.yaml
dependencies:
  # ...

dev_dependencies:
  # ...

patrol:
  app_name: Awesome App
  android:
    package_name: pl.leancode.awesomeapp
  ios:
    bundle_id: pl.leancode.AwesomeApp

If your app has different name on iOS and Android, you can specify app_name twice – one in android block, and one in ios block.

Of course, replace these placeholders with values specific to your app!

We highly recommend performing this step, because it enables the following features:

  • Patrol will automatically uninstall your app after every test (using package_name and bundle_id). This will make the environment which your tests run in more stable and predictable.
  • Patrol will be able to tap on your app's notifications (using app_name)

Install patrol_cli#

Patrol CLI (command-line interface) is a small program that makes it easier to run Flutter integration tests using Patrol's native automation feature. It handles the complexity of native iOS and Android testing tools itself, so you can focus on writing your tests instead of learning yet another platform-specific tool.

  1. Install patrol executable:

    dart pub global activate patrol_cli
    
  2. Verify that installation was successful:

    patrol --version
    

Also make sure to update Patrol CLI from time to time:

patrol update

Create a simple integration test#

Let's create a dummy Flutter integration test that you'll use later to verify that Patrol is correctly set up.

Paste the following code into integration_test/example_test.dart:

integration_test/example_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:patrol/patrol.dart';

void main() {
  patrolTest(
    'counter state is the same after going to home and switching apps',
    nativeAutomation: true,
    ($) async {
      // Replace later with your app's main widget
      await $.pumpWidgetAndSettle(
        MaterialApp(
          home: Scaffold(
            appBar: AppBar(title: const Text('app')),
            backgroundColor: Colors.blue,
          ),
        ),
      );

      expect($('app'), findsOneWidget);
      await $.native.pressHome();
    },
  );
}

It does only 2 things:

  • first, it finds a widget with the app key
  • then, it then exits to home screen

This is enough to verify if the setup is done correctly.

Integrate with native side#

The 3 first steps were common across platforms. The rest is platform-specific.

Psst... Android is a bit easier to set up, so we recommend starting with it!

Dart-side logs are currently not printed during patrol test. We're aware of this issue and will fix it. Use flutter logs as a temporary workaround.

What did you just do?#

If you've diligently followed the above steps and patrol test prints a TEST PASSED message, you might be now thinking: what did I just do?

The answer is: You've just integrated Flutter testing with native Android/iOS testing frameworks. This means that your Flutter integration tests can now be run as native tests.

What are native tests good for, anyway?#

iOS and Android have existed for more than 15 years, and during that time many of awesome testing-related things were built around them – open-source test runners, device farms, HTML report generators. Developers who create native mobile apps can easily reap benefits from this huge, mature ecosystem.

Meanwhile we, Flutter developers, don't have as much at our disposal. Our framework is much younger and less mature.

What if we could masquerade our Flutter tests so that from the outside they would be truly native? This way we leverage many existing tools while maintaining the convenience of writing the tests in pure Dart.

For example, you can run your Patrol tests directly from Xcode. Xcode knows nothing about Flutter, Dart and Patrol – it only launches your test app. Flutter tests are then run inside the test app and the results are reported back to Xcode. This way you get the best of both worlds – the maturity of native iOS development and the produtivity of Flutter and Dart.

That's exactly what Patrol does (and what the default integration_test package does at well, but at a bit smaller scale).

Take a look at this simple Flutter integration tests using Patrol:

integration_test/example_test.dart
void main() {
  patrolTest(
    'counter state is the same after going to Home and switching apps',
    nativeAutomation: true,
    nativeAutomatorConfig: NativeAutomatorConfig(
      packageName: 'pl.leancode.patrol.example',
      bundleId: 'pl.leancode.patrol.Example',
    ),
    ($) async {
      await $.pumpWidget(ExampleApp());

      await $(FloatingActionButton).tap();
      expect($(#counterText).text, '1');

      await $.native.pressHome();
      await $.native.openApp();

      expect($(#counterText).text, '1');

      await $(FloatingActionButton).tap();
      expect($(#counterText).text, '2');
    },
  );
}

You can run this test and view its results in many ways, using all sorts of different tools, platforms, and IDEs:

This is so awesome!

Going from here#

To learn more about native capabilities, see native automation section.