Skip to main content
Ready to supercharge your Flutter integration tests? This guide will help you migrate from Flutter’s built-in integration_test package to Patrol, unlocking powerful native automation capabilities and improved test ergonomics.

Why Migrate to Patrol?

Patrol extends Flutter’s integration_test with game-changing features:
  • Native Platform Access: Interact with permission dialogs, notifications, WebViews, and system settings
  • Better Finders: Cleaner, more intuitive syntax for finding widgets
  • Hot Restart: Fast test iteration with patrol develop
  • Test Isolation: Proper test isolation and sharding support
  • Device Farm Support: Works with Firebase Test Lab, BrowserStack, LambdaTest, and more
  • Web Testing: Test Flutter web applications (Patrol 4.0+)
  • DevTools Extension: Inspect native views directly in Flutter DevTools
Patrol is fully compatible with integration_test - you can use both in the same project during migration.

Prerequisites

Before starting the migration:
  • Flutter SDK 3.16 or higher (required for Patrol 4.x)
  • Existing tests using Flutter’s integration_test package
  • Basic understanding of Flutter integration testing

Migration Overview

The migration process involves:
1

Install Patrol

Add Patrol CLI and package to your project
2

Configure Patrol

Set up pubspec.yaml and native integration
3

Convert Tests

Update test code to use Patrol APIs
4

Verify

Run and validate migrated tests

Step 1: Install Patrol

Install Patrol CLI

Patrol requires a dedicated CLI tool for running tests:
flutter pub global activate patrol_cli
Make sure to add patrol to your PATH environment variable. Check the installation guide for platform-specific instructions.
Verify installation:
patrol doctor
Expected output:
Patrol CLI version: 2.3.1+1
Android:
• Program adb found in /Users/username/Library/Android/sdk/platform-tools/adb
• Env var $ANDROID_HOME set to /Users/username/Library/Android/sdk
iOS / macOS:
• Program xcodebuild found in /usr/bin/xcodebuild
• Program ideviceinstaller found in /opt/homebrew/bin/ideviceinstaller

Add Patrol Package

Add patrol to your dev_dependencies:
flutter pub add patrol --dev
You can keep integration_test in your dependencies during migration to run both test types.

Step 2: Configure Patrol

Update pubspec.yaml

Add a patrol configuration section to your pubspec.yaml:
pubspec.yaml
name: my_app
description: My Flutter app

dev_dependencies:
  flutter_test:
    sdk: flutter
  integration_test:
    sdk: flutter
  patrol: ^4.0.0  # Add this

# Add patrol configuration
patrol:
  app_name: My App
  android:
    package_name: com.example.myapp  # Your Android package name
  ios:
    bundle_id: com.example.MyApp      # Your iOS bundle ID
Find your package name in android/app/build.gradle (look for applicationId) and bundle ID in ios/Runner.xcodeproj/project.pbxproj (look for PRODUCT_BUNDLE_IDENTIFIER).

Configure Test Directory (Optional)

By default, Patrol looks for tests in patrol_test/, but you can configure it to use your existing integration_test/ directory:
pubspec.yaml
patrol:
  app_name: My App
  test_directory: integration_test  # Use existing directory
  android:
    package_name: com.example.myapp
  ios:
    bundle_id: com.example.MyApp
We recommend creating a separate patrol_test/ directory to keep Patrol tests separate from legacy integration_test tests during migration.

Set Up Native Integration

Patrol requires native platform integration for full functionality.

Step 3: Convert Your First Test

Let’s migrate a simple integration test to Patrol.

Before: integration_test

integration_test/app_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  testWidgets('counter increments', (WidgetTester tester) async {
    app.main();
    await tester.pumpAndSettle();

    // Find widgets
    final counterFinder = find.text('0');
    final buttonFinder = find.byIcon(Icons.add);

    // Verify initial state
    expect(counterFinder, findsOneWidget);

    // Tap button
    await tester.tap(buttonFinder);
    await tester.pumpAndSettle();

    // Verify counter incremented
    expect(find.text('1'), findsOneWidget);
  });
}

After: Patrol

patrol_test/app_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:patrol/patrol.dart';
import 'package:my_app/main.dart';

void main() {
  patrolTest('counter increments', ($) async {
    // Pump app
    await $.pumpWidgetAndSettle(const MyApp());

    // Verify initial state
    expect($('0'), findsOneWidget);

    // Tap button using Patrol's finder
    await $(Icons.add).tap();

    // Verify counter incremented
    expect($('1'), findsOneWidget);
  });
}

Key Differences

integration_testPatrol
IntegrationTestWidgetsFlutterBinding.ensureInitialized()Not needed - automatic
testWidgets()patrolTest()
WidgetTester testerPatrolIntegrationTester $
app.main()await $.pumpWidgetAndSettle(MyApp())
find.text('0')$('0')
find.byIcon(Icons.add)$(Icons.add)
tester.tap() + tester.pumpAndSettle()await $(widget).tap()
Patrol’s finders automatically handle settling after actions, making tests more concise.

Step 4: Advanced Migration Patterns

Finding Widgets

// Find by text
find.text('Submit');

// Find by key
find.byKey(Key('submit_button'));

// Find by type
find.byType(ElevatedButton);

// Find by icon
find.byIcon(Icons.search);

// Multiple widgets
find.text('Item').at(1);

Interacting with Widgets

// Tap
await tester.tap(find.text('Submit'));
await tester.pumpAndSettle();

// Enter text
await tester.enterText(
  find.byType(TextField),
  'Hello',
);
await tester.pumpAndSettle();

// Scroll
await tester.scrollUntilVisible(
  find.text('Item'),
  500.0,
);

Waiting and Expectations

// Wait for widget
await tester.pumpAndSettle();
expect(find.text('Success'), findsOneWidget);

// Wait with timeout
await tester.pumpAndSettle(
  const Duration(seconds: 5),
);

Step 5: Leverage Patrol’s Native Automation

Now for the exciting part - add native automation that wasn’t possible with integration_test:
patrol_test/app_test.dart
patrolTest('handle permissions and notifications', ($) async {
  await $.pumpWidgetAndSettle(const MyApp());

  // Grant permission when dialog appears
  if (await $.platform.mobile.isPermissionDialogVisible()) {
    await $.platform.mobile.grantPermissionWhenInUse();
  }

  // Trigger a notification
  await $(#notificationButton).tap();

  // Go to home screen
  await $.platform.mobile.pressHome();

  // Open notifications
  await $.platform.mobile.openNotifications();

  // Tap on the notification
  await $.platform.mobile.tapOnNotificationBySelector(
    Selector(textContains: 'New message'),
    timeout: Duration(seconds: 5),
  );

  // Verify app responds
  expect($(#notificationHandled), findsOneWidget);
});
This level of native interaction is impossible with integration_test alone!

Step 6: Run Your Migrated Tests

Running Patrol Tests

# List available devices
patrol devices

# Run tests (interactive device selection)
patrol test

# Run specific test file
patrol test --target patrol_test/app_test.dart

# Run on specific device
patrol test -d emulator-5554

# Development mode with hot restart
patrol develop --target patrol_test/app_test.dart
Use patrol develop during test development for fast iteration with hot restart!

Running in CI

.github/workflows/test.yml
name: Patrol Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v3
      
      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.16.0'
      
      - name: Install Patrol CLI
        run: flutter pub global activate patrol_cli
      
      - name: Get dependencies
        run: flutter pub get
      
      - name: Run Patrol tests
        run: patrol test --device <device-id>
        env:
          CI: true

Migration Checklist

Use this checklist to track your migration progress:
1

Setup

  • Install Patrol CLI with patrol doctor passing
  • Add patrol to dev_dependencies
  • Configure patrol section in pubspec.yaml
  • Set up Android native integration
  • Set up iOS native integration
2

Test Conversion

  • Create patrol_test/ directory (or configure test_directory)
  • Convert testWidgets() to patrolTest()
  • Replace WidgetTester with PatrolIntegrationTester
  • Update finders to use $() syntax
  • Remove manual pumpAndSettle() calls after actions
  • Replace app.main() with $.pumpWidgetAndSettle()
3

Verification

  • Run tests with patrol test
  • Verify all tests pass
  • Test on multiple devices/platforms
  • Update CI configuration
4

Enhancement

  • Add native automation where beneficial
  • Use patrol develop for faster iteration
  • Try Patrol DevTools extension
  • Explore advanced features (sharding, tags, etc.)

Gradual Migration Strategy

You don’t have to migrate everything at once:
1

Run Both Test Types

Keep both integration_test and patrol in your project:
dev_dependencies:
  integration_test:
    sdk: flutter
  patrol: ^4.0.0
2

Migrate Incrementally

Migrate one test file at a time:
  • Move file from integration_test/ to patrol_test/
  • Convert the test code
  • Verify it works
  • Move to next file
3

Prioritize High-Value Tests

Start with tests that will benefit most from Patrol:
  • Tests that need permission handling
  • Tests that interact with notifications
  • Tests with complex scrolling
  • Tests that need native automation

Troubleshooting

Common Issues

Key Takeaways

Patrol is a superset of integration_test: All your existing integration test knowledge applies. Patrol adds:
  • Better finder syntax
  • Native automation capabilities
  • Improved development experience
  • Better test isolation and reliability

Next Steps

Now that you’ve migrated to Patrol, explore its powerful features:

Need Help?

If you encounter issues during migration: Welcome to the Patrol community! Happy testing!

Build docs developers (and LLMs) love