Webinar: Mastering Patrol & AI: Next-Level E2E Testing. Register now

build

Synopsis

Build app binaries for integration testing.

patrol build android
patrol build ios

To see all available options and flags, run patrol build android --help or patrol build ios --help.

For patrol build to work, you must complete native setup.

Description

patrol build is useful if you want to run test on CI, for example on Firebase Test Lab. It works the same as patrol test, except that it does not run tests.

patrol build builds apps in debug mode by default.

To run tests on a physical iOS device on a device farm, the apps have to be built in release mode. To do so, pass the --release flag.

Examples

To build a single test for Android in debug mode

patrol build android --target patrol_test/example_test.dart

or alternatively (but redundantly):

patrol build android --target patrol_test/example_test.dart --debug

To build all tests for Android in debug mode

patrol build android

To build a single test for iOS device in release mode

patrol build ios --target patrol_test/example_test.dart --release

To build a single test for iOS simulator in debug mode

patrol build ios --target patrol_test/example_test.dart --debug

To build with custom build name and number

patrol build android --build-name=1.2.3 --build-number=123
patrol build ios --build-name=1.2.3 --build-number=123  --release

To build with full isolation between tests

patrol build ios --full-isolation

The --full-isolation flag enables full isolation between test runs on iOS Simulator.

Build-time test discovery

With the experimental --emit-test-manifest flag (or patrol.emit_test_manifest: true in pubspec.yaml), patrol build discovers your Dart tests at build time and generates a static native test method for each one. This makes every test individually addressable, which enables per-test sharding and patrol test-without-building.

patrol build android --emit-test-manifest
patrol build ios --emit-test-manifest

See Build-time test discovery for setup (including the required iOS RunnerUITests.m change) and trade-offs.

Building for a patrol develop session on another machine

patrol develop normally builds the app itself. With --develop, patrol build android produces the same two APKs patrol develop would build (the app and the androidTest APK) for a single --target, with Hot Restart enabled. That lets you build once — for example on CI — and iterate elsewhere without Gradle:

patrol build android --develop --target patrol_test/example_test.dart

Then, on a checkout of the same commit using the same Flutter version, point patrol develop at a directory that contains exactly those two APKs. If you pass --test-server-port or --app-server-port, pass the same values to both commands - the ports are baked into the APKs at build time:

patrol develop --use-prebuilt-apks path/to/apks --target patrol_test/example_test.dart

No Gradle or APK build runs on that machine. Patrol compiles the Dart bundle once with flutter build bundle --debug (which also generates the Dart plugin registrant that flutter attach needs on a never-built checkout), installs the APKs with adb, starts the Patrol instrumentation with am instrument, connects flutter attach, and hot restarts the requested target in place of the test that was bundled at build time. From there the session behaves exactly like a regular patrol develop — edit the test (or Dart app code), press r, repeat. Also works from Patrol MCP via PATROL_FLAGS=--use-prebuilt-apks=<dir>.

Hot Restart only replaces Dart code, so the sources must otherwise match the build: same Flutter/Dart SDK (a different one fails on the device with Invalid kernel binary format version), same --dart-defines and --flavor, same Patrol server ports. Native changes (plugins, manifest, permissions) still require a new build. Android only.

Under the hood

The patrol build command walks through hierarchy of the patrol_test directory and finds all files that end with _test.dart, and then creates an additional "test bundle" file that references all the tests it found. Thanks to this, all tests are built into a single app binary - only a single build is required, which greatly reduces time spent on building. Then, it runs a new app process for every test, improving isolation between tests and enabling sharding.

We call this feature advanced test bundling. It provides deep and seamless integration with existing Android and iOS testing tools. It also fixes some long-standing Flutter issues:

We think that this is huge (even though it may not look like it at first glance). To learn more, read the in-depth technical article explaining the nuts and bolts.

On this page