FlutterモバイルアプリのE2Eテストを提供したい
ウイングアークAgile and DevOps Stories のAdvent Calendar 2021、第7弾(12月9日)の投稿となります!!
E2Eテストはじめました
私は9月からソフトウェア&品質改善部にジョインしQAをはじめました。
まず取り掛かったのは Flutterで作られたモバイルアプリのE2Eテストの自動化です。
ここではFlutter のintegration_packageと、Appium について検証した結果を記載しています。
私はこの仕組みづくりのために、多くのドキュメント、Issues、Blog等を参考にさせていただきました。この記事はそのお返しとしてノウハウの再還元をさせていただくものです。
なお、ここで説明している動作や情報は Flutter 2.3.0–16.0.preのものであり、情報として多少古くなっています。ご了承ください。(現在はFlutter 2.5.3 (stable) が最新となっています。)
integration_testによるE2Eテスト
はじめに検討したのは Flutter が標準で提供しているintegration_testパッケージ(参考:An introduction to integration testing | Flutter)を利用するテストの方式です。開発 アプリと同じ技術スタックを使えるので有力な候補です。
私は「公式の提供だし、ドキュメントもあるし、これなら簡単にできそうだな。」と喜びましたが、それは長く続きませんでした。この先、課題にぶつかっては解決 していくということが続いていきます。
Keyの埋め込み
何らかのE2Eテストをされた方は理解されていると思いますが、自動化するために操作する対象アプリの要素を識別子で特定する必要があります。Flutterでは Keyを用います(参考:Find widgets | Flutter) そのためKeyが不足しているところを埋めていきます。
本体のコードに手を加えることになりますが、品質は全員で作り込んでいくものですのでQAも積極的にコードを書いてよいと思っています。また、開発の方もコードに手を加えることにOKを出してくれました。
WebViewの突破
テストの実装を進めると、WebViewの要素が特定できず、テストが難しいことが分かりました。(参照:#34345、#86295)
少し悩むところではありますが、本体にJavaScriptを埋め込んで処理を回避することにします。このJavaScriptはWebViewが表示されたときに実行され、直接HTMLを操作するものです
問題の解消には、下記の記事を参考にさせていただきました。
- https://stackoverflow.com/questions/67687971/how-to-automate-a-flutter-application
- https://medium.com/flutter-community/flutter-webview-javascript-communication-inappwebview-5-403088610949
このコードは本体を修正しているため、E2Eテストとしては不適切ですし、プロダクトのコードとしてはマージできなくなります。Mockすることでコードを切り離せそうではありますが、いったん課題として残しておきます。
pumpAndSettleが効かない
Flutterでは描画フレームがなくなるまで pump してくれるpumpAndSettleがあり、これを挟んで次のステップに進むようにテストを書きますが、上記WebView を超えたあたりで使えなくなりました。私の技術不足により原因が特定できませんでしたので、固定ウェイトにして逃げることにします。
これもまた課題として積み上げます。
追記:ある特定のWidgetが描画し続けることが原因でした。Flutter Inspector を使うことで見つけることができました。
スクリーンショットが撮れない
E2Eテストのエビデンスは画像で残したいのですが、当バージョンではスクリーンショットはまだ撮れませんでした。この pull #84472以降のバージョンでは取得できるようになります。ただ、別の問題#51890もあるようです。
これも課題として残しておきます。
追記:Flutter 2.5.3 のAndroid でスクリーンショットを取れることを確認しました。
AppiumによるE2Eテスト
integration_test でE2Eテストが動くことが確認できましたが、課題も多いことが分かりました。ここでは他にも目を向け Appiumでも確認していきます。
Appium Inspectorによる要素の確認
Appium Inspector は、アプリを操作しながらテストコードのベースを作ることができるので、テストコードを作る際に利用しました。実際にFlutterアプリの操作を始めてみるとすぐに気がつくのですが、要素がXPathになってしまうためよろしくありません。
FlutterアプリですのでKeyで指定したいところです。
appium-flutter-driverを利用する
FlutterのKeyを指定するためにappium-flutter-driver を使うことにしました。これで Keyを指定したテストができるようになります。またAppiumのクライアントは appim-java-clientを使うことにしました。
Flutter以外を使うことにより、開発が持つ技術スタックが使えなくなってしまうことは課題として残しておきます。
appium-flutter-driver のサンプルのpom.xmlでは、Maven Repositoryが使えそうな記載になっていましたが実際には提供されておらず (参考:issues#120)、こちらの issues#101からダウンロードする必要がありました。ソースコードも無いようです。ローカルに格納後、pom.xmlから systemで設定します。
<dependency>
<groupId>pro.truongsinh</groupId>
<artifactId>appium-flutter-finder</artifactId>
<version>0.0.4</version>
<scope>system</scope>
<systemPath>${basedir}/lib/appium-flutter-finder0.0.4.jar</systemPath>
</dependency>
全量はこちらです。
ソースコードが公開されてないことと、更新頻度が不明な点は課題として積み上げます。
Keyを指定したテストコード用意する
appium-flutter-driverを使うことで、テストコードは次のようにKeyを指定できるようになりました。
本体コードの修正
Appiumを使ってテストをするために、やはり本体のコードを修正する必要がありました。
appium-flutter-driver のUsageより:
Also, ensure that your Flutter AUT has enableFlutterDriverExtension() before runApp.
また、このenableFlutterDriverExtension によりキー入力を受け付けなくなるため、開発環境とE2Eテスト環境の切り替えに時間がかかるようになります。コードは次のようになります。
import 'package:flutter_driver/driver_extension.dart';Future<void> main() async {
enableFlutterDriverExtension(); // ここ
(snip)
flavorなどを用いて分離できそうですが、課題として残します。
WebViewの突破とスクリーンショットの取得
Flutterで課題となったWebViewは、コンテキストを切り替えとXPathを指定することで突破できました。こちらは本体コードにJavaScriptを埋め込まなくてもOKでした。
driver.context("NATIVE_APP");
MobileElement el9 = (MobileElement)
driver.findElementByXPath("/(snip)/android.widget.EditText");
el9.sendKeys("sendValue");
driver.context("FLUTTER");
スクリーンショットの取得も、同様にコンテキストを切り替えることで可能です。
driver.context("NATIVE_APP");
Files.copy(driver.getScreenshotAs(OutputType.FILE),
new File(screenshotPath + "ScreenShot001.png"));
driver.context("FLUTTER");
AI自動テストツールについて
ローコード/ノーコードテストツールや、AI自動テストツールと言われる製品についても調査をしましたが、Flutterでは要素の検出やアサーションが不十分のようで、まだFlutterモバイルアプリへの利用は難しいように思います。(もし、良いものをご存知でしたら教えて下さい。)
どれが良いか
integration_test や Appiumを使ったE2Eテストはある程度できることが確認できました。しかし、それぞれに課題があることも分かりました。
- integration_testパッケージの利用では、「WebViewの突破のため本体の修正が必要」、「pumpAndSettleが効かない」、「スクリーンショットが撮れない」という課題がありました。
- Appiumの利用では、「開発が持つ技術スタックが使えない」「appium-flutter-driverのJavaのソースコードがなく更新頻度が不明」、「本体の修正が必要で、開発環境とE2Eテスト環境の切り替えに時間がかかる」という課題がありました。
今回調べた範囲ではいづれも扱いにくい点があり、判断が難しく思います。
Flutterのバージョンを上げることによって結果が異なってくるでしょうし、近いうちにAI自動テストツールも良くなっていくでしょうから、時間をおいて再評価したいと思います。
最後に
私がE2Eテストの問題にぶつかったときに、開発の方や上司・同僚を始めとして、ネットやコミュニティのさまざまな方の情報を参考にさせていただきました。これら情報を提供していただいた方に感謝しています。
私がこの記事を書こうと思ったのも、その恩返しがしたいと思ったからです。いくつかの課題とその手当を示しました。この内容が誰かの参考になれば幸いです。