Skip to content

FlutterProbe vs Patrol: Which Flutter E2E Framework to Choose

Patrol and FlutterProbe both solve problems that Flutter’s built-in integration_test cannot. Both handle native OS dialogs. Both give you access to the Flutter widget tree. But they take fundamentally different architectural approaches, and that difference shapes everything from who writes the tests to how they run in CI.

This page is a technical comparison to help you decide which framework fits your team.

Patrol, developed by LeanCode, extends integration_test with two key additions:

  1. NativeAutomator. A platform-specific automation layer that can tap native UI elements — permission dialogs, system settings, the notification shade — that integration_test cannot reach.
  2. Custom finders. The $('text') syntax replaces verbose find.text('text') calls, making Dart test code shorter.

Patrol tests are Dart files that live inside your Flutter project. They compile and run as part of the app process, just like integration_test. This means you get direct widget-tree access with the addition of native automation.

patrolTest('user grants camera permission and takes photo', ($) async {
await $.pumpWidgetAndSettle(MyApp());
await $('Take Photo').tap();
await $.native.grantPermission();
await $.native.tap(NativeSelector(text: 'Allow'));
expect($('Photo Preview'), findsOneWidget);
});

Patrol is a solid choice for Dart-native teams that need native dialog handling without leaving the Flutter test ecosystem.

FlutterProbe uses a split architecture: a Go CLI runs on the host machine and sends commands over WebSocket to a Dart agent embedded in the app. Tests are written in ProbeScript, a plain-English DSL stored in .probe files outside the app project.

test "user grants camera permission and takes photo"
open the app
tap "Take Photo"
grant permission camera
see "Photo Preview"

The Go CLI handles device management, test orchestration, reporting, and cloud-farm integration. The Dart agent handles widget-tree queries and actions. This separation means the test code never compiles into the app binary in release builds.

AspectPatrolFlutterProbe
Test languageDartProbeScript (plain English)
Where tests liveInside app projectSeparate .probe files
Test runnerFlutter test runnerGo CLI (probe run)
Device communicationIn-processWebSocket + JSON-RPC
Command round-tripIn-process (nanoseconds)Sub-50ms over WebSocket
App compilationTests compile with appAgent compiles with app; tests do not
CLI dependencyFlutter CLIFlutterProbe CLI + Flutter CLI

Patrol’s in-process model means zero network overhead per command. FlutterProbe’s WebSocket round-trip adds roughly 10-40ms per command, but the total test duration is usually dominated by animations and network calls, not command overhead. In practice, both frameworks produce comparable end-to-end run times.

Patrol reduces Dart boilerplate compared to raw integration_test, but tests are still Dart code with imports, async/await, and programmatic control flow.

FlutterProbe’s ProbeScript is intentionally constrained. Each line is a single command or assertion. There are no variables, loops, or conditionals in the base language. When you need programmatic logic, you use hooks (written in Dart or shell scripts) or recipes to encapsulate reusable flows.

This trade-off is deliberate. ProbeScript optimizes for readability by non-developers at the cost of expressiveness. If every person writing tests is a Dart developer, Patrol’s syntax may feel more natural. If tests need to be reviewed or authored by QA engineers, product managers, or designers, ProbeScript removes the language barrier.

FeaturePatrolFlutterProbe
Native dialog handlingYes (NativeAutomator)Yes (built-in commands)
Widget-tree accessYes (in-process)Yes (via Dart agent)
Custom finders$('text') syntaxText, key, semantic label, type
Self-healing selectorsNoYes
Visual regressionNoYes
Test recordingNoYes
HTTP mockingManual setupBuilt-in
GPS / location mockingManualset location lat, lng
Clipboard controlManualset clipboard "text"
Parallel executionNo--parallel, --shard
Cloud farm supportLimited5 farms (BrowserStack, Firebase Test Lab, AWS Device Farm, Sauce Labs, LambdaTest)
CI reportingManualHTML + JUnit built-in
Migration toolingNo7 formats (Maestro, Gherkin, Detox, Appium, Robot, integration_test, Patrol)
VS Code extensionNoYes (syntax highlighting, CodeLens, test explorer)
Data-driven testsManual (Dart loops)Built-in (CSV, JSON)
HooksDart setUp/tearDownbefore all/each, after all/each, on failure

Both frameworks access the widget tree directly, so element lookups are fast on both sides. The main performance differences come from test orchestration:

  • Patrol compiles tests into the app binary. Changing a test requires recompiling the app, which can take 30-90 seconds on large projects.
  • FlutterProbe compiles only the Dart agent into the app. ProbeScript files are interpreted at runtime by the Go CLI. Changing a test requires no recompilation — just re-run the command. This makes the edit-run cycle significantly faster during test development.

For CI runs where the app is compiled once and all tests execute sequentially, compilation overhead is amortized and both frameworks are comparable. For parallel runs, FlutterProbe’s --shard flag distributes tests across devices, which Patrol does not support natively.

Patrol relies on the Flutter test runner and outputs results to stdout. Getting JUnit XML or HTML reports requires third-party packages or custom scripts.

FlutterProbe’s CLI generates reports natively. A typical GitHub Actions workflow looks like:

- name: Run E2E tests
run: probe run tests/ --device emulator-5554 --report junit --report html
- name: Upload report
uses: actions/upload-artifact@v4
with:
name: test-report
path: probe-reports/

Reports include screenshots on failure, step-by-step timing, and pass/fail summaries. The reports documentation covers all output formats.

Choose Patrol if:

  • Your entire team writes Dart and prefers staying in one language.
  • You need native automation but want to stay close to the standard Flutter test API.
  • Your test suite is small to medium (under 50 tests) and runs on local devices.
  • You do not need visual regression, self-healing selectors, or cloud-farm execution.

Choose FlutterProbe if:

  • Non-developers (QA, product, design) need to read or write tests.
  • You run tests on cloud device farms or need parallel execution across multiple devices.
  • You want visual regression testing and self-healing selectors to reduce maintenance.
  • You are migrating from another framework (Maestro, Detox, Appium, Gherkin) and want automated conversion.
  • Fast iteration matters — no recompilation when tests change.

If you have existing Patrol tests, FlutterProbe’s probe-convert tool can translate them into ProbeScript:

Terminal window
probe convert --from patrol --input tests/patrol/ --output tests/probe/

The converter handles patrolTest blocks, $() finders, NativeAutomator calls, and common assertions. Review the output for edge cases, then run the converted tests with probe run.

  1. Install the CLI — a single binary, no Node.js or Gradle plugins required.
  2. Follow the Quick Start to write your first .probe test.
  3. Explore the ProbeScript syntax reference for the full command set.
  4. Set up the VS Code extension for syntax highlighting and one-click test runs.