Platforms
Flutter
Build and test a Flutter app on the iOS Simulator or the Android emulator with danwo, and what its network and locators need.
danwo builds a Flutter app for the iOS Simulator and the Android emulator, and runs your tests on it like any other app. The app's calls go through danwo's proxy, so the matrix can fault them.
Use it when your app is written once in Flutter and you want the same tests, and the same faults,
on both platforms. The with-flutter example runs one test file on
both.
Build the app
npx @danwo/cli build-ios .
npx @danwo/cli build-android .Both find the app's pubspec.yaml and run Flutter's own build. Each build is kept by a hash of the
app's sources, ios/ and android/ included, and reused until they change.
| Platform | What danwo runs | What it keeps |
|---|---|---|
| iOS | flutter build ios --simulator --debug | Runner.app, with plain HTTP allowed, and its bundle id |
| Android | flutter build apk --config-only, then the project's gradlew assembleRelease | the release APK, and its package name |
Flutter builds simulator apps in debug mode only. On Android, danwo adds its network config to the APK for this build only, as it does for native Android apps: the APK trusts the certificate danwo installs on the emulator. Your project's files are not changed, and no Gradle or Kotlin daemon is left running.
Call through the platform's HTTP stack
Dart's own HttpClient connects directly. It ignores the device's proxy setting and the
certificates installed on it, so its calls skip danwo's proxy.
Use the platform's own HTTP stack instead: cupertino_http on iOS and cronet_http on Android.
Both follow the device's proxy, as a native app does. Their clients work with package:http, so
the rest of the app's code stays the same.
import 'dart:io';
import 'package:cronet_http/cronet_http.dart';
import 'package:cupertino_http/cupertino_http.dart';
import 'package:http/http.dart';
Client httpClient() {
if (Platform.isIOS) return CupertinoClient.defaultSessionConfiguration();
if (Platform.isAndroid) return CronetClient.defaultCronetEngine();
return Client();
}A release APK needs the INTERNET permission in android/app/src/main/AndroidManifest.xml.
Flutter's template adds it to the debug manifest only.
Find the app's elements
Give an element a Semantics identifier, and getByTestId finds it on both platforms. Buttons
and text are found by their role and label, as in any app.
Semantics(
identifier: 'email',
child: TextField(
controller: _email,
decoration: const InputDecoration(labelText: 'Work email'),
),
),await screen.getByTestId("email").fill("[email protected]")
await screen.getByRole("button", { name: "Join the waitlist" }).tap()On Android, Flutter hands a text field's label to the accessibility tree only as its hint, and
danwo can't read hints. So find text fields by their identifier: getByLabel finds them on iOS
only.
Run the tests on either platform
One config can hold both devices. The with-flutter example picks one from an environment
variable, so the same tests run on each:
npx @danwo/cli test
DANWO_PLATFORM=ios npx @danwo/cli test ✓ tests/deterministic.danwo.ts › shows the waitlist form 3380 ms, worker 0
✓ tests/deterministic.danwo.ts › asks for a valid email 5948 ms, worker 0
✓ tests/deterministic.danwo.ts › joins the waitlist 4221 ms, worker 0The fault matrix runs the same way. In the example, all 18 faults it plans pass on the emulator and on the simulator: the app retries a failed join, and the API keeps one row and one email.