2019/7/25のAndroidTestNightで発表した資料です
リリース前のリグレッションテストをUIテストで自動化、1年間運用した話養命酒(Yu Mitsuhori)/ @1013YoumeeeAndroid Test Night #7 at DeNA
View Slide
自己紹介2- 三堀 裕(みつほり ゆう)- 養命酒(youmeee, youmitsu)- メインはAndroid- 新宿の隣でヘルスケア系アプリを開発していました(7/19に最終出社でした。有給消化中)- Github: youmitsu- Twitter: @1013Youmeee
この発表の目的- 某アプリLでのAndroid Instrumented Testを用いたリグレッションテストの自動化の取り組みについての事例の紹介- 1年間運用してみてよかった点や、注意点、課題点などの紹介3
こんな話をしていきます- 自動化の経緯- 某アプリLでのテスト- テストの種類- タイミング- 構成と構築について- テスト方針- システム構成図- 選定技術とテスト実装について- 1年間運用してみた所感- 良かった点、現時点での課題点、改善策- まとめ4
自動化の経緯5
- 2015年にリニューアル- MAU約400万- 中〜大規模- 開発チーム:Android 2人, iOS 3人- QAチームなどはいない。開発チームが仕様検討、設計、実装、テスト、リリースを一貫して行う- ベースはClean Architecture6某アプリLの概要
某アプリLの悩みの種- 保守運用フェーズ、開発人員も減らされている- これからも長く存続しつづけるであろう- 開発人員が少ない中での作業効率化(自動化できるものはしていきたい)- テスト実施における精神的負担の軽減7
某アプリLでのリグレッションテスト8
某アプリLでのリグレッションテスト- リリース前に改修していない部分に影響がないかをテストする(E2Eテスト)=>手動での実施だと全ケース約1人日かかる参考- 単体テスト- 各レイヤーごと、View以外- 結合テスト- 修正された機能においてテストケースを作成し、実機を操作してテストする9
主な開発フローとテスト10DevelopFeature1Feature2ReleaseMaster: 単体テスト: 結合テスト: リグレッションテストPlayStoreへデプロイ
自動テスト構築について11
当初の自動化方針- 基本的な方針は今までのリグレッションテストと同じ- E2Eテスト。できるだけ実働環境に近い状態で実行する- Releaseブランチにて実施する- 自動化できない部分は手動で実施する- 主にWebView- TestSuiteはActivityやFragment単位ではなく、ユーザタイプ&課金タイプごとに定義する- APIやWebサーバはテスト環境を使用- テストファーム(Visual Studio AppCenter)で走らせることを想定12
テスト実行時の構成- Android Instrumented Test(AndroidJUnitRunner)- Espresso- Assertion, Action- UiAutomator- バックグラウンド復帰の実装- RxIdler2- RxJava2のBackground Taskの終了を待つ- AppCenter SDK- 画面のキャプチャ- Page Object Pattern(リファクタリング中)- 画面ごとをObjectとして定義する。UIテストにおけるデザインパターン13
テストコード実装にあたって- アプリをインストールしてからすべての機能を順番にチェックしていく- 画面に表示されているコンポーネントのチェック(Espresso)- onView()で要素取得(条件はwithIdやwithText)- isDisplayed()による表示確認- 実際にユーザが操作するのと同じシチュエーションで実行したいため、API通信はモックせず実際のネットワーク通信でテストを実行する14
アーキテクチャ15Runner TestCase Pageテストを実行するクラス。エントリポイント各Feature単位で定義。Pageオブジェクトを使ってユーザシナリオを組み立てる画面単位で定義。assert,perform系のメソッドを持ち、TestCaseから呼び出される1 n nn
簡単なコードイメージ(Runnerクラス)@RunWith(AndroidJUnit4.class)@LargeTestpublic class RegressionRunner {@Inject NoticeTest noticeTest; // 各機能ごとにテストケースクラスを定義@Rule public ReportHelper reportHelper = Factory.getReportHelper(); // AppCenterSDKの画像キャプチャ用インスタンス@Rule public ActivityTestRule mainActivityActivityTestRule = new ActivityTestRule(MainActivity.class, false, false);@Test// ゲストユーザ&&TypeAのシナリオテストを実行public void GuestUserTypeA_regression() {reportHelper.label("A_freeUser_regression Start"); // AppCenterSDKによる画面キャプチャsplashActivityActivityTestRule.launchActivity(new Intent()); // アプリの起動noticeTest.assertNotice(); // お知らせ画面のテスト実行...}// 有料ユーザ&&TypeBなどのシナリオテストを以下に続けて定義}16Runnerクラス
簡単なコードイメージ(TestCase, Pageクラス)class NoticePage : Page {private fun isShowedCouponBanner() =onView(withId(R.id.coupon_view)).check(matches(isDisplayed()))fun clickBanner() = apply {onView(withId(R.id.coupon_view)).perform(click())label("clickBanner")}}17Pageクラスclass NoticeTest @Inject constructor(context: Context) : BaseTest(context) {fun assertNotice() {NoticePage().assert {isShowedCouponBanner()}.clickBanner().assert {// クリック後の画面状態をassert}}}TestCaseクラス
システム構成について18
自動化におけるシステム構成19ScheduleBuild & DistributeAppTest appRun Test & ReportAzure PipelineVisual StudioApp CenterOutlookFlowTeamsDeveloperNotificationテスト完了通知はメールでしか受け取れないためCheck Result以下のIPを許可しておく・195.249.159.238/32・195.249.159.239/32
AppCenterの紹介- Microsoftが提供するモバイル向けCI/CDプラットフォーム- Build, Test, Distributeなどテスト- DeviceFarm上で実行可能- デバイス数が豊富- 100ドルずつ課金していくと並列実行数を増やせる- 直列のみ: 98ドル, 2並列: 198ドル, n並列: 98nドル- 30日間は無料トライアル期間- AppCenterSDKを使うと、スクリーンショットの撮影も可能(動画は撮れない...はず)20
1年間運用してみた所感21
良かった点- 全ケース中の60%ほどテスト工数が削減できた- 手動でやる作業が削減されたことで、効率的、精神衛生的にもGood- とりあえずこれを回しておけばOKという点では安心感もある- テスト実施頻度が高くなった- カスタムビューで実装していた部分はEspressoのonViewで取得できないことがあったので、UIテストにおいてのテスタビリティを意識するきっかけになった22
辛い点、出てきた問題点- 不安定(特にAppCenterでの実行時)通らないとリリースできないので、逆にリリースまでのスピードが落ちる- ネットワーク起因(タイムアウト)=> API通信のモック化を検討中- AppCenterの実機がアニメーションオフにしていない- 安定しないので、手動でスケジューリングすることになる、、=> 安定してきたらCIとして自動スケジューリングさせたい- 実行速度が遅い(テスト実行: 15~20分、テスト結果作成: 15~20分)- 3つのOSバージョンで直列実行するため、全部終わるのに 1時間くらいかかる- WebView辛い- 非同期にレンダリングされる要素は取得に失敗しやすい( Espresso Web)23
まとめ24
まとめ- 某アプリLにおいて、リリース前のリグレッションテストを自動化した事例について紹介- UIテスト安定化させるのは難しい、継続的にテスト自体をメンテしていく必要がありそう- 通信のタイムアウト- デバイス起因- 実行時間- WebView- いくつかの工夫によって安定化を目指していく- WebViewに対しては深いテストをしない- APIClientをモックする25