⚠️ Independent fork —Bdaya-Dev/patrolis an independent fork of Patrol, originally created by LeanCode. It is maintained by Bdaya-Dev and is not maintained, supported, or endorsed by LeanCode. Please report issues at https://github.com/Bdaya-Dev/patrol/issues, not to LeanCode. Both the original and this fork are licensed under the Apache License 2.0 (see LICENSE and NOTICE.md).
A powerful, multiplatform E2E UI testing framework for Flutter apps that overcomes the limitations of integration_test by handling native interactions, battle-tested and shaped by production-grade experience.
Note
Patrol 4.7.0 adds Swift Package Manager support for iOS and macOS! If you migrate your project to SPM, a few small setup changes are needed — see the iOS setup guide for details.
Flutter's finders are powerful, but not very intuitive to use.
We took them and made something awesome.
Thanks to Patrol's custom finders, you'll take your tests from this:
testWidgets('signs up', (WidgetTester tester) async {
await tester.pumpWidget(AwesomeApp());
await tester.pumpAndSettle();
await tester.enterText(
find.byKey(Key('emailTextField')),
'charlie@root.me',
);
await tester.pumpAndSettle();
await tester.enterText(
find.byKey(Key('nameTextField')),
'Charlie',
);
await tester.pumpAndSettle();
await tester.enterText(
find.byKey(Key('passwordTextField')),
'ny4ncat',
);
await tester.pumpAndSettle();
await tester.tap(find.byKey(Key('termsCheckbox')));
await tester.pumpAndSettle();
await tester.tap(find.byKey(Key('signUpButton')));
await tester.pumpAndSettle();
expect(find.text('Welcome, Charlie!'), findsOneWidget);
});to this:
patrolTest('signs up', (PatrolIntegrationTester $) async {
await $.pumpWidgetAndSettle(AwesomeApp());
await $(#emailTextField).enterText('charlie@root.me');
await $(#nameTextField).enterText('Charlie');
await $(#passwordTextField).enterText('ny4ncat');
await $(#termsCheckbox).tap();
await $(#signUpButton).tap();
await $('Welcome, Charlie!').waitUntilVisible();
});Learn more about custom finders in packages/patrol_finders_plus!
Patrol's custom finders are also available standalone in the patrol_finders package.
Flutter's default integration_test package can't interact with the OS your Flutter app is running on. This makes it impossible to test many critical business features, such as:
- granting runtime permissions
- signing into the app which through WebView or Google Services
- tapping on notifications
- much more!
Patrol's native automation feature solves these problems:
void main() {
patrolTest('showtime', (PatrolIntegrationTester $) async {
await $.pumpWidgetAndSettle(AwesomeApp());
// prepare network conditions
await $.platform.mobile.enableCellular();
await $.platform.mobile.disableWifi();
// toggle system theme
await $.platform.mobile.enableDarkMode();
// handle native location permission request dialog
await $.platform.mobile.selectFineLocation();
await $.platform.mobile.grantPermissionWhenInUse();
// tap on the first notification
await $.platform.mobile.openNotifications();
await $.platform.mobile.tapOnNotificationByIndex(0);
});
}
The CLI is needed to enable Patrol's native automation feature in integration tests. It also makes development of integration tests much faster thanks to Hot Restart.
To run widget tests, you can continue to use flutter test.
See packages/patrol_plus.
See .github/WORKFLOWS.md for detailed documentation about all GitHub Actions workflows, including test schedules, Flutter versions, and deployment pipelines.
- (Optionally) add new request type:
class OpenAppRequest {
late String appId;
}- Add new method to
NativeAutomator:
abstract class NativeAutomator<IOSServer, AndroidServer, DartClient> {
...
void openApp(OpenAppRequest request);
...
}- Run
gen_from_schemascript, few files will be updated
If you have previously activated patrol_cli_plus run:
dart pub global deactivate patrol_cli_plusthen
cd packages/patrol_cli_plus
flutter pub global activate -s path .