[Flutter] integration testで発生しがちなつまづきと解決方法
IT技術
はじめに
以前、以下の記事でflutter integration testの導入について紹介しました。
[Flutter] integration test でiOS/Androidアプリのテストを自動化!(導入編)
あれからMagicPodがついにFlutter対応したり…ということもありましたが、手元で手軽にテストができるFlutter integration testの魅力もあります!
(MagicPodは、MagicPodクラウドのシュミレータで動くアプリファイルを出力しないといけないですし…)
手軽とはいえどE2Eテスト、安定して動くように構築するには様々な課題があります。
性質上、不具合の原因を追いかけるのも、事前知識がないと難しいことも多々あり…自分が遭遇したFlutter integration testで発生しがちな問題と、その原因、解決方法をまとめていきます。
Flutter integration Testにおけるデメリットとも言えますので、導入前の参考にもどうぞ!
よくある不具合
■expectに失敗している訳でもないのに、急にテストが失敗することがある
原因:Flutterアプリ本体からExceptionが出ているかも
Flutter Integration TestがExceptionをcatchすると、すべて「テストの失敗」として処理されてテストが終了します。ですので、アプリ本体側で些細なExceptionが出ているのを放置していると、テストドライバ側がそれを拾ってしまっている可能性があります。
解決方法:アプリ本体側でちゃんとExceptionの処理をする
もしくは、テストドライバが拾う前にどこかで処理をする仕組みを作ると、テストの安定性自体は増します。
■アプリに行った操作が、テスト開始後に反映されていない
原因:テストシナリオ内で操作行われていない
テストの安定性を担保するため、テストシナリオ(testWidgets単位)ごとに毎回アプリはアンインストール、再ビルドされます。そのため、アプリインストール処理(pumpWidget)の前に行ったアプリの変更はすべて初期化されます。
解決方法:シナリオごとに初期設定を行うようにする
■flutter test でまとめてテストを行った際、2つめ以降が失敗する
原因:必要な初期設定が、ひとつめのテストシナリオ内でのみ行われている
flutter test <folder>
コマンドでは、フォルダ内のテストをまとめて実行できたりと便利ではありますが、処理上、テストシナリオごとにアプリが完全に初期化されます。ひとつめのテストを行ったあと、その状態を引き継いで次のテストシナリオを行うことはできません。
解決方法:シナリオごとに初期設定を行うようにする
■アプリ内で表示された通知、位置情報などの使用許可のモーダルを操作できない
原因:Flutter integration testはFlutterアプリ外のUIを一切操作できない
Flutter integation TestではFlutterアプリ外の機能、ブラウザやOSが表示しているアラート、モーダルなどの操作ができません。
tester.tapで画面内の特定の座標にタップ判定を出すこともできますが、この機能を使用したとしてもタップできないです。
解決方法:事前にpermissionの許可を出してテストを開始する
iOSシュミレータであればxcrun simctl(xcrun simctl privacy
)、Androidエミュレータであればadb(adb shell pm grant
)を使用して、事前にパーミッション権限を承認してからテストシナリオを開始するようにします。
自分の環境だとアプリがインストールされていない状態でも承認設定を行えたので、アプリインストール処理後(pumpWidget後)でなくてもよいかもしれないです。
□xcrun simctlが機能しない
xcrun simctlですが、いくつか機能していないものがあります。自分の場合、AppleSimulatorUtilsで対応可能でした。
xcrun simctl でサポートされている機能は非推奨として、今後の保守は行わないと明記されていますが、xcrun simctlが正常動作するまではこちらを代用できるかもしれません。
■pumpandstillでテストが進まなくなる、もしくは待機してくれない
原因:pumpandstillは処理上、画面の変更が行われているかのみを検知します
描画待ちがなくなるまで待機し続ける関数であるため、常に画面上の何かがアニメーションしている場合は待機し続けますし、画面がどこも変更されていない場合は何かしらの処理中であっても待機を解除して次に進みます。
解決方法:描画待機用の関数を作成する
pumpandstilが使用できないからとpumpを使用すると、描画や通信のバッファを取るためにテスト時間が伸びていきます。
単純に要素が表示されるまで待ちたい場合は、以下のような処理を別途で用意して使用するほうが良いです。
1await pumpUntilFound(tester, find.byKey(ValueKey("MainScreen")));
2
3Future pumpUntilFound(
4 WidgetTester tester,
5 Finder finder, {
6 Duration timeout = const Duration(seconds: 30),
7}) async {
8 bool timerDone = false;
9 final timer = Timer(timeout, () => throw TimeoutException("Pump until has timed out"));
10 while (timerDone != true) {
11 await tester.pump();
12
13 final found = tester.any(finder);
14 if (found) {
15 timerDone = true;
16 }
17 }
18 timer.cancel();
19}
参考: https://github.com/flutter/flutter/issues/73355
さいごに
UIテストツールはツールごとになにかと癖や特徴がありますが、Flutterと同じDartで描かれているために、自分のアプリにあった実装をいれやすいというのもFlutter integration testのよいところだと思います。
今後導入される方の参考になれば幸いです。
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
おすすめ記事
immichを知ってほしい
2024.10.31