skip to content

For Flutter integration tests, when do you run flutter drive with a driver script instead of flutter test integration_test, and what does the driver do?

level: middleimportance: should knowfreq 36%

answer

  1. a host-side Dart program
  2. test_driver/integration_test.dart calls integrationDriver
  3. requestData, then exit 0 or 1
  4. reportData lands in build/integration_response_data.json
  5. web, screenshots, timelines

basics

~20 s

flutter test integration_test is the default runner. Use flutter drive when the host must receive data from the test - screenshots, performance timelines, custom reportData - or when targeting the web; its driver script runs on the host and collects that data.

solid answer

~40 s

`flutter test integration_test` builds, installs and reports results itself - enough for pass or fail on a device. `flutter drive --driver=test_driver/integration_test.dart --target=integration_test/checkout_test.dart` adds a **host-side Dart program**, usually just `Future<void> main() => integrationDriver();`. It connects to the app with `FlutterDriver.connect()`, waits for the results with `requestData` (20-minute default timeout), then exits 0 or 1. Its `responseDataCallback` receives the binding's `reportData` map; the default, `writeResponseData`, writes `build/integration_response_data.json`. I choose drive when the host needs artefacts: screenshots through `integrationDriver(onScreenshot: ...)` from `integration_test_driver_extended.dart`, timelines from `traceAction` summarised on the host, custom `reportData`, a `--profile` build for performance, or a web run against chromedriver with `-d web-server` or `-d chrome`, which `flutter test` does not support.

code

dart · 15 lines
dart
// test_driver/integration_test.dart
import 'dart:io';

import 'package:integration_test/integration_test_driver_extended.dart';

Future<void> main() async {
  await integrationDriver(
    onScreenshot: (String name, List<int> bytes, [Map<String, Object?>? args]) async {
      final File file = File('build/screenshots/$name.png');
      await file.parent.create(recursive: true);
      await file.writeAsBytes(bytes);
      return true;
    },
  );
}

go deeper

for a junior

Recall the two commands: flutter test integration_test for plain runs, flutter drive with --driver and --target when a host-side driver script is needed.

for a middle

Explain the driver's flow - connect, requestData, callback, exit code - and how reportData, screenshots and timelines travel from the device to the host.

for a senior

Pick the runner per need in CI: flutter test for pass or fail, drive for artefacts, profile runs and web, with explicit timeouts and a driver that stores outputs.

for a principal

Standardise what integration runs must produce - results only, or screenshots and performance data - since that choice decides the runner and pipeline complexity.

## Two ways to run the same test file An `integration_test/*.dart` file is the same whichever command starts it. What differs is **who collects the result**. | | `flutter test integration_test` | `flutter drive --driver ... --target ...` | |---|---|---| | Host-side code | none; the tool reports results | your driver script in `test_driver/` | | Output | test runner output, exit code | exit code plus whatever the driver writes | | Web targets | rejected | supported via a WebDriver server | | Screenshots to the host | no | yes, via `onScreenshot` | | Timelines and custom `reportData` | not collected | passed to `responseDataCallback` | | Several files in one command | yes | one `--target` per run | | Typical use | functional checks on devices | artefacts, performance, web | ## What the driver script is A **driver** is a Dart program that runs on your computer, not on the device. The minimal one lives at `test_driver/integration_test.dart`: ```dart import 'package:integration_test/integration_test_driver.dart'; Future<void> main() => integrationDriver(); ``` `integrationDriver` does four things: 1. `FlutterDriver.connect()` to the app that `flutter drive` launched. 2. `driver.requestData(null, timeout: timeout)` - it waits (default **20 minutes**) until the on-device tests finish and the integration binding returns a JSON response. 3. If all tests passed it calls `responseDataCallback(response.data)` and exits **0**; otherwise it prints the failure details and exits **1** (calling the callback on failure only when `writeResponseOnFailure` is true). 4. The default callback, **`writeResponseData`**, writes the data as `integration_response_data.json` into the test outputs directory - `build` unless `FLUTTER_TEST_OUTPUTS_DIR` says otherwise. ## Where the data comes from On the device, the test writes into the binding's **`reportData`** map - a `Map<String, dynamic>?` of JSON-serialisable values that starts as `null`: - `binding.takeScreenshot('checkout-summary')` adds an entry under `screenshots`; - `binding.traceAction(() async { ... }, reportKey: 'checkout_scroll')` stores a timeline under that key (default `timeline`); - `binding.watchPerformance(() async { ... })` stores a frame-timing summary (default key `performance`); - the test can add its own keys, such as the order number it created. That map travels to the driver with the results. Without a driver, nobody on the host receives it. ## Screenshots For screenshots, import `package:integration_test/integration_test_driver_extended.dart` in the driver and pass `onScreenshot: (String name, List<int> bytes, [Map<String, Object?>? args]) async { ...; return true; }`. Returning `false` fails the run, which lets the driver reject bad images. On Android the test must call `binding.convertFlutterSurfaceToImage()` and pump a frame before `takeScreenshot`. ## Useful `flutter drive` options - `--driver` and `--target` pick the host script and the on-device test. - `-d` picks the device; `-d web-server` runs a headless web session against a WebDriver server such as chromedriver on port 4444 (`--driver-port`). - `--profile` builds in profile mode for performance measurement; the performance cookbook adds `--no-dds` on mobile devices. - `--timeout` (seconds, no default) stops a stuck run; with `--screenshot <dir>` a screenshot is taken on failure or timeout. ## Choosing - Checkout journey must pass on an emulator in CI, result only: **`flutter test integration_test`**. - Need the confirmation screen as a PNG artefact, a scroll timeline, or the web build: **`flutter drive`** with a driver script. - Remember the driver is ordinary Dart on the host: it can post-process, upload or compare what it receives.

  • Where does integration_response_data.json come from, and when is it empty?
    It is written by `writeResponseData`, the default `responseDataCallback` of `integrationDriver`, from the binding's `reportData`. It is only written when all tests pass (unless `writeResponseOnFailure` is true), and it contains `null` data if the test never wrote anything to `reportData`.
  • Why can't the on-device test just write screenshots to the CI machine's disk?
    It runs inside the app on the emulator or device, with that device's filesystem. The bytes have to travel back over the driver connection; `takeScreenshot` puts them in `reportData`, and the driver's `onScreenshot` callback receives them on the host and writes them.

saying these in an interview costs you the question

  • The driver script runs inside the app on the device
  • flutter drive is deprecated and flutter test does everything it does
  • reportData reaches the host even when running flutter test integration_test
  • flutter test integration_test can target Chrome directly
  • integrationDriver writes response data even when a test failed, by default