Skip to main content
Patrol 4.0 introduces the Platform Automation API and begins the deprecation of legacy native automation entry points. This guide provides comprehensive migration instructions to help you transition your tests smoothly.
$.native, $.native2, NativeAutomator, NativeAutomator2, and NativeAutomatorConfig are deprecated and will be removed in a future release. Migrate to $.platform and PlatformAutomatorConfig.

Why Migrate?

The new Platform Automation API offers:
  • Unified API: Single, consistent interface across all platforms
  • Web Support: Test Flutter web applications alongside mobile
  • Better Organization: Clear separation between cross-platform and platform-specific actions
  • Type Safety: Improved type checking and better IDE support
  • Future-Proof: Active development and long-term support

Understanding the New API Structure

The Platform Automation API is organized into clear namespaces:
$.platform.mobile    // Cross-platform mobile actions (Android + iOS)
$.platform.android   // Android-only actions
$.platform.ios       // iOS-only actions
$.platform.web       // Web automation
$.platform.tap(...)  // Platform router (automatically selects platform)
In most cases, you’ll use $.platform.mobile for cross-platform mobile automation.

Breaking Changes You’ll Likely Hit

Here are the most common changes you’ll need to make:

Entry Point Changes

// Using $.native
await $.native.tap(Selector(text: 'Allow'));
await $.native.pressBack();

// Using $.native2
await $.native2.pressHome();
await $.native2.grantPermissionWhenInUse();

Selector Changes

NativeSelector(
  android: AndroidSelector(resourceName: 'submit_button'),
  ios: IOSSelector(identifier: 'submitButton'),
)

Quick Migration Checklist

Follow this checklist to ensure complete migration:
1

Update Patrol

# Update Patrol CLI
patrol update

# Update patrol package
flutter pub upgrade patrol
2

Rename Test Directory (Optional)

Rename integration_test/ to patrol_test/ and update .gitignore:
mv integration_test patrol_test
Or configure to keep using integration_test/:
pubspec.yaml
patrol:
  test_directory: integration_test
3

Update Native Automation Calls

Replace all deprecated API calls:
  • $.native.*$.platform.mobile.* or platform-specific
  • $.native2.*$.platform.mobile.*
4

Update Selector Types

Replace:
  • NativeSelector(...)MobileSelector(...), Selector(...), or PlatformSelector(...)
5

Update CI Configuration

Add device selection to avoid prompts:
patrol test --device <device-id>
# or set CI=true environment variable
6

Test Your Migration

patrol test

Common Migration Patterns

Pressing Home Button

await $.native2.pressHome();

Platform-Specific Actions

Some actions are platform-specific and need the appropriate namespace:
await $.native.pressBack(); // Android only
await $.native.closeHeadsUpNotification(); // iOS only

Understanding Selector Types

Patrol 4.0 actions accept different selector types depending on the context. Here’s when to use each:

Selector(...) - Universal Selector

Use when the same selector works across all platforms (most common for text-based selectors):
await $.platform.mobile.tap(Selector(text: 'Allow'));
await $.platform.mobile.tap(Selector(textContains: 'Submit'));
Selector is the simplest and most portable option. Use it whenever possible.

MobileSelector(...) - Mobile-Only Selector

Use when Android and iOS require different selectors:
await $.platform.mobile.tap(
  MobileSelector(
    android: AndroidSelector(resourceName: 'com.example:id/submit_button'),
    ios: IOSSelector(identifier: 'submitButton'),
  ),
);
This is the direct replacement for NativeSelector from Patrol 3.x.

PlatformSelector(...) - Cross-Platform Selector

Use with the platform router ($.platform.tap) to support Android, iOS, and Web:
await $.platform.tap(
  PlatformSelector(
    android: AndroidSelector(text: 'Click Android text'),
    ios: IOSSelector(text: 'Click iOS text'),
    web: WebSelector(cssSelector: '#submit-button'),
  ),
);
PlatformSelector can also be used with $.platform.mobile.* methods as long as it includes both Android and iOS selectors.

Detailed Migration Examples

Example 1: Permission Dialogs

patrolTest('handle permissions', ($) async {
  await $.pumpWidgetAndSettle(MyApp());
  
  // Check for permission dialog
  if (await $.native2.isPermissionDialogVisible()) {
    await $.native2.selectFineLocation();
    await $.native2.grantPermissionWhenInUse();
  }
});

Example 2: Notifications

patrolTest('tap notification', ($) async {
  await $.pumpWidgetAndSettle(MyApp());
  
  // Trigger notification
  await $(#notificationButton).tap();
  
  // Go home and open notifications
  await $.native2.pressHome();
  await $.native2.openNotifications();
  
  // Tap notification
  await $.native2.tapOnNotificationBySelector(
    Selector(textContains: 'New message'),
    timeout: Duration(seconds: 5),
  );
});

Example 3: Device Settings

patrolTest('toggle settings', ($) async {
  await $.pumpWidgetAndSettle(MyApp());
  
  // Disable Wi-Fi
  await $.native2.disableWifi();
  
  // Enable dark mode
  await $.native2.enableDarkMode();
  
  // Test app behavior
  expect($(#offlineMessage).visible, true);
});

Example 4: Custom Automator with Teardown

patrolTearDown(() async {
  final automator = NativeAutomator(config: nativeConfig);
  
  // Reset settings
  await automator.enableWifi();
  await automator.enableCellular();
  await automator.disableDarkMode();
});

Configuration Migration

If you’re using custom automator configuration:
final nativeConfig = NativeAutomatorConfig(
  keyboardBehavior: KeyboardBehavior.alternative,
);

final automator = NativeAutomator(config: nativeConfig);

Search and Replace Guide

Use these patterns for bulk migration:
FindReplace
$.native.tap($.platform.mobile.tap(
$.native.$.platform.mobile. (review each for platform-specific)
$.native2.$.platform.mobile.
NativeSelector(MobileSelector(
NativeAutomator(PlatformAutomator(
NativeAutomatorConfig(PlatformAutomatorConfig.fromOptions(
Always review replacements manually, especially for platform-specific actions like pressBack() (Android) and closeHeadsUpNotification() (iOS).

Platform-Specific Actions Reference

Android-Only Actions

These actions should use $.platform.android.*:
await $.platform.android.pressBack();
await $.platform.android.pressRecentApps();

iOS-Only Actions

These actions should use $.platform.ios.*:
await $.platform.ios.closeHeadsUpNotification();

Cross-Platform Mobile Actions

These actions work on both Android and iOS using $.platform.mobile.*:
await $.platform.mobile.pressHome();
await $.platform.mobile.openNotifications();
await $.platform.mobile.grantPermissionWhenInUse();
await $.platform.mobile.enableWifi();
await $.platform.mobile.enableDarkMode();
// ... and many more

Testing Web Applications

With Patrol 4.0, you can now test web applications:
patrolTest('web authentication', ($) async {
  await $.pumpWidgetAndSettle(MyWebApp());
  
  // Grant browser permissions
  await $.platform.web.grantPermissions(permissions: ['clipboard-read']);
  
  // Add cookies
  await $.platform.web.addCookie(name: 'session', value: 'abc123');
  
  // Handle browser dialogs
  await $.platform.web.acceptNextDialog();
  
  // Interact with web elements
  await $.platform.web.tap(WebSelector(cssSelector: '#login-button'));
});
Learn more about web testing in the Web Testing Guide.

Troubleshooting

”Method not found” Errors

If you see errors about methods not being found:
  1. Check you’re using the correct namespace (mobile, android, ios, or web)
  2. Verify the method exists in Patrol 4.0 (some may have been renamed)
  3. Ensure you’ve updated to the latest Patrol version

Type Errors with Selectors

If you get type errors with selectors:
// Wrong - NativeSelector is deprecated
await $.platform.mobile.tap(NativeSelector(...)); 

// Correct - Use MobileSelector
await $.platform.mobile.tap(MobileSelector(...));

// Or use Selector for text-based
await $.platform.mobile.tap(Selector(text: 'Button'));

Platform-Specific Action on Wrong Platform

If Android-specific actions fail on iOS (or vice versa):
// Wrong - pressBack is Android-only
await $.platform.mobile.pressBack();

// Correct - Use platform-specific namespace
await $.platform.android.pressBack();

// Or wrap in platform check
if (Platform.isAndroid) {
  await $.platform.android.pressBack();
}

Migration Validation

After migration, verify your tests work correctly:
1

Run All Tests

patrol test
Ensure all tests pass without deprecation warnings.
2

Check for Deprecation Warnings

Review test output for any deprecation warnings and update those usages.
3

Test on Multiple Platforms

# Test on Android
patrol test -d emulator-5554

# Test on iOS
patrol test -d iPhone-Simulator
4

Verify CI Pipeline

Ensure your CI pipeline runs successfully with the new API.

Benefits of the New API

After migration, you’ll benefit from:
  • Clearer code: Explicit platform namespaces make intent obvious
  • Better errors: Improved error messages when actions fail
  • Web support: Ability to test Flutter web applications
  • Type safety: Stronger typing catches errors at compile time
  • Future features: New capabilities will be added to the Platform API

Need Help?

If you encounter issues during migration:

Next Steps

After completing the migration:

Build docs developers (and LLMs) love