[Flutter] Patrol 逆引き大全 (E2Eテスト)
FlutterのE2Eテストにはさまざまな議論がありますが、私のプロジェクトではPatrol
を採用しています。本記事では、公式のサンプルアプリを参考にしながら、Patrol
を使ったテストの具体例をメソッド別に、逆引きで検索できる形式でまとめました。
目次から操作したい機能を選び、各機能ごとにテストコードの例を確認できるようになっているため、実際の開発プロジェクトでのテスト設計に役立てていただければ幸いです。
※公式のサンプルアプリで使用されているような、使用頻度が高いものをまとめています。
こんな人におすすめ
・E2EテストでNative操作をしたい
・Patrolを効率よく使いたい
・Patrolでどんなことができるのか知りたい
単体テストの考え方について知りたい方は、こちらもぜひ!
Patrol について
Patrolは、Flutterのintegration_test
に対する強力な代替として、より実際の環境に近い自動テストを提供するフレームワークです。
Patrolでは、OSの操作やシステム設定の変更が可能なほか、Nativeの画面をSelectorを通して識別でき、通知やWebViewのテストも実施できます。また、コードがintegration_test
と類似しているため、直感的でわかりやすいです。
セットアップ
環境
- M3 Macbook Pro
- Flutter 3.24.4 • channel stable
- patrol_cli: 3.2.1
- patrol: 3.11.2
- patrol_finders: 2.1.3
- java version: 17.0.13
- Android 14 (API 34) (emulator)
公式のサンプルを参照しよう!
公式のサンプルアプリをcloneして、一度ローカルで動かしてみることをお勧めします!
ビルドコマンド
$ patrol test -t integration_test/example_test.dart
基本
keyを識別
ex)Textに、keyを指定
Text(
key: const Key('key'),...
),
Textの文字を取得
$(#key).text
指定された要素が画面に表示されるまで待機
await $.waitUntilVisible($(#keyname));
描画が終わるのを待つ
await $.pumpAndSettle();
指定時間待つ
await $.pump(Duration(seconds: 5));
TextFieldに文字を入力
await $(#keyname).enterText('Hello, Flutter!');
タップ
await $.tap($(#keyname));
n番目をタップ
.at(n).tap();
特定のwidgetが表示されるまでスクロール
await $.scrollUntilVisible(
finder: $(#keyname), scrollDirection: AxisDirection.down);
特定の文字が表示されるまで自動スクロール
ex)listでindex番号を文字として表示するときに、100番目までスクロール
await $('index: 100').scrollTo();
特定の文字を含むwidgetが存在するか確認する
expect($('Hello, Flutter!'), findsOneWidget);
widgetが存在しないことを確認する
expect($("Can't touch this"), findsNothing);
特定の数のwidetが存在することを確認する
expect(find.byType(Card), findsNWidgets(3));
Nativeの機能を操作しよう!
ホームに戻る
await $.native.pressHome();
アプリを開く
await $.native.openApp();
Androidの場合は通知シェード、iOSの場合は通知センターを開く
await $.native.openNotifications();
通知をタップする
await $.native.tapOnNotificationBySelector(
Selector(textContains: 'Someone liked'),
);
別のアプリを開く
ex) chromeのアプリを開くケース
browserId = 'com.android.chrome';
await $.native.openApp(appId: browserId);
ディープリンク
await $.native.openUrl('patrol://check/somepath?query=10');
Androidではクイック設定シェード、iOSではコントロールセンターを開く
await $.native.openQuickSettings();
bluetoothのオンオフ
await $.native.disableBluetooth();
await $.native.enableBluetooth();
機内モードのオンオフ
await $.native.disableAirplaneMode();
await $.native.enableAirplaneMode();
セルラー接続(モバイルデータ)のオンオフ
await $.native.disableCellular();
await $.native.enableCellular();
wifiのオンオフ
await $.native.disableWifi();
await $.native.enableWifi();
システムのダークモードのオンオフ
await $.native.disableDarkMode();
await $.native.enableDarkMode();
位置情報のオンオフ
await $.native.disableLocation();
await $.native.enableLocation();
音量のアップダウン
await $.native.pressVolumeUp();
await $.native.pressVolumeDown();
Nativeの画面をスワイプする
ex) 設定のページ
appId = 'com.android.settings';
// 設定アプリを開く
await $.native.openApp(appId: appId);
// スワイプ
await $.native.swipe(
from: Offset(0.5, 0.8),
to: Offset(0.5, 0.2),
appId: appId,
);
Nativeの画面をタップする
appId = 'com.android.settings';
// 設定アプリを開く
await $.native.openApp(appId: appId);
// 画面をタップする
await $.native.tapAt(
Offset(0.5, 0.8),
appId: appId,
);
「おおよその位置情報」を許可をセレクト
※位置情報許可ダイアログ出現時
await $.native.selectCoarseLocation();
「正確な位置情報」を許可をセレクト
※位置情報許可ダイアログ出現時
await $.native.selectFineLocation();
パーミッションを操作しよう!
const _timeout = Duration(seconds: 5);
アクセス許可を否定する
※ _timeoutの設定も可能
if (await $.native.isPermissionDialogVisible(timeout: _timeout)) {
await $.native.denyPermission();
}
アプリの使用中は許可する
if (await $.native.isPermissionDialogVisible()) {
await $.native.grantPermissionWhenInUse();
}
一度だけ許可
if (await $.native.isPermissionDialogVisible()) {
await $.native.grantPermissionOnlyThisTime();
}
Webviewを操作しよう!
ボタンをタップする
ex) webサイトが表示された時に出る
Accept all cookies のボタンをタップする。
await $.native.tap(Selector(text: 'Accept all cookies'));
Android の EditText,iOS の TextField または SecureTextFieldに文字を入れ込む
await $.native.enterTextByIndex(
'test@gmail.com',
// n番目のテキストフィールド
index: 0,
keyboardBehavior: KeyboardBehavior.alternative,
);
native2について
native2
を使うことで、AndroidとiOSのネイティブ機能に対して統一された操作が可能です。
await $.native2.tap(
NativeSelector(
android: AndroidSelector(text: 'Log in'),
ios: IOSSelector(label: 'Log in'),
),
);
nativeAutomatorとnativeAutomator2
$.nativeは、nativeAutomator
の省略形であり
$.native2は、nativeAutomator2
の省略形です。
native2
は、アプリが実行されている OS とのやり取りを可能にする新しいセレクター API を備えたネイティブオートメーションです。
このように、AndroidとiOSで異なるプロパティ(textやlabel)を指定することで、OSごとの画面構造の違いを意識せずに、同じ操作を行えるようになります。native2
を使えば、AndroidとiOSに対して統一されたテストが可能です。
まとめ
native機能を活用したテストは、ユーザーのエクスペリエンスをリアルに再現できるので、アプリの信頼性が向上します。E2Eテストには労力がかかりますが、品質向上に不可欠です。ぜひ、Patrol
の機能を活用して、効率よくテストを実施してみてください⭐️
アップデートされた時は、随時更新していければと思います。
Discussion