Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DroidKaigi2017 - 変更に強いEspressoテストコードを効率良く書こう /...

DroidKaigi2017 - 変更に強いEspressoテストコードを効率良く書こう / DroidKaigi2017 - Let's Write Sustainable Espresso Test Rapidly

DroidKaigi 2017で発表した
「変更に強いEspressoテストコードを効率良く書こう」(3/9 16:00-16:50 @ Room4)
のスライドです。

ライブコーディングが含まれますので、ビデオも合わせてご覧ください。
https://goo.gl/iKJcmg

実演に利用するサンプルアプリのソースコードは、以下より入手してください。
https://github.com/sumio/espresso-sample-for-droidkaigi2017

また、発表中に紹介したユーティリティメソッドだけを切りだしたものは、こちらから入手できます。
https://github.com/sumio/espresso-commons

TOYAMA Sumio

March 09, 2017
Tweet

More Decks by TOYAMA Sumio

Other Decks in Technology

Transcript

  1. ⾃⼰紹介 p ⽒名: 外⼭ 純⽣ (TOYAMA Sumio) @sumio_tym (Twitter) /

    @sumio (GitHub) p 所属: NTTソフトウェア株式会社 p 業務内容: p 社内Androidプロジェクトの技術⽀援 p 社内Androidプロジェクトのテスト⾃動化⽀援 p プライベート: p STAR(テスト⾃動化研究会) p @IT連載「スマホ向け無料システムテスト⾃動化 ツール」(UI Automator / Appiumの回を担当) 2
  2. 話の流れ 1.イントロダクション p 効率的にテストを書くこと p 変更に強いテストを書くこと 2.Espressoの基本 3.Espresso Test Recorderとその限界

    4.実践!変更に強いテストを効率的に書く p サンプルアプリについて p テスト⾃動化対象の選定 p 操作・検証の記録 p 修正 4
  3. 10 䗤䗣䗋䗞䗚䗬 䗖䗡䗡䗄䗛 䗬䗞䗩䗝䖸䗡䗖䗨䗨䗃䗖䗢䗚 䗞䗨 5FYU7JFXDMBTTHFU/BNF 䗞䗨䖹䗚䗨䗘䗚䗣䗙䗖䗣䗩䗄䗛䖶 䗝䗖䗨䗈䗞䗗䗡䗞䗣䗜 䗝䗖䗨䖹䗚䗨䗘䗚䗣䗙䗖䗣䗩 䗬䗞䗩䗝䗉䗚䗭䗩

    ✿᧠ DIFDL 䗢䗖䗩䗘䗝䗚䗨 䗬䗞䗩䗝䗉䗚䗭䗩    ↓を⾃分でひねり出すのは正直厳しい... UIテスト実装時のボトルネック(イメージ)
  4. 変更に弱いテストの典型例(イメージ) 17 ログイン画⾯のテストケースを考える 1. ID/PWを⼊れて「ログイン」と表⽰されたボタン を押すと〜されること 2. PWが未⼊⼒で「ログイン」と表⽰されたボタン を押すと〜されること 3.

    IDが未⼊⼒で「ログイン」と表⽰されたボタン を押すと〜されること 後で「ログイン」ボタンが「OK」ボタンに 仕様変更されると、全部修正が必要!
  5. ここまでのまとめ 効率的にテストを書くためには pEspresso Test Recorderの得意箇所から着⼿ p 得⼿不得⼿を知っておく必要がある 変更に強いテストにするには pEspresso Test

    Recorderの出⼒コードから、 ⼿作業で共通部分を抽出 p リファクタ機能を駆使した具体的な修正⽅法を 修得しておく必要がある 22
  6. Espressoの概要 p Android公式のUIテストフレームワーク p コードの簡潔さ・信頼性の⾼さに定評あり p テスト実⾏速度が⽐較的速い p WebViewにも対応 p

    URL phttps://goo.gl/x8eP5C phttps://goo.gl/lmrlJI 24 (※) https://google.github.io/android-testing-support-library/docs/espresso/cheatsheet/ よりロゴを引⽤ (※)
  7. Espressoのセットアップ ビルド環境設定(https://goo.gl/WlXSCq) p Android Support Repositoryのインストール p build.gradleのdependenciesブロック (以下の2つのモジュールを宣⾔) p

    configuration: androidTestCompile p group: com.android.support.test.espresso p module: 1. espresso-core (Recorderが⾃動設定する) 2. espresso-contrib (⼿動で追加する) 25
  8. Espressoのセットアップ 26 androidTestCompile( 'com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations'

    } ) プロダクトコードとコンフリクト するモジュールを除外する。 (ビルドエラー時のメッセージを参照)
  9. Espressoのセットアップ 27 androidTestCompile( 'com.android.support.test.espresso:espresso-contrib:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations'

    exclude group: 'com.android.support', module: 'support-v4' exclude group: 'com.android.support', module: 'appcompat-v7' exclude group: 'com.android.support', module: 'design' exclude group: 'com.android.support', module: 'recyclerview-v7' } )
  10. Espressoのテストコード p src/androidTest/java に配置 p Android Testing Support Libraryに従う ※

    Espresso Test Recorderが雛型を⾃動⽣成 29 @RunWith(AndroidJUnit4.class) public class MyEspressoTest { @Rule public ActivityTestRule<...> activityTestRule = ...; ... }
  11. Espressoで操作・確認できること p Button、EditText、TextView、ダイアログな どの基本的なコンポーネント p ListView - onData() p RecyclerView

    – RecyclerViewActions ※ViewHolder内のViewの確認には⼯夫が必要 p NavigationDrawer・NavigationView - DrawerActions・NavigationViewActions p DatePicker・TimePicker - PickerActions p WebView内HTML - onWebView() https://goo.gl/OMfVyZ 参照 32
  12. ここまでのまとめ 環境設定は、 Espresso Test Recorderに任せるのが楽 p 端末のアニメーションOFFなど⼿動設定も必要 ⼤部分のUI部品の操作・確認をサポート p 公式にチートシートあり

    以下については近付かないのが無難 p Viewプロパティの動的な取得 p 操作・確認対象が明確でないケース p 別画⾯遷移後の⾮同期待ち合わせ 35
  13. 操作の記録 p 起動したテスト対象アプリを直接操作する p 操作した内容は「Record Your Test」ダイ アログに反映されていく (応答が遅いので、1アクションの都度、反 映されるまで待つのがお奨め)

    39 ※以降、 https://github.com/googlecodelabs/android-testing で公開されているサンプルアプリを使ったスクリーンショットを掲載しています。
  14. 記録の終了 43 p 「Record Your Test」 >「OK」 p テストクラス名を⼊⼒ ※

    build.gradleが未設定だと、 このタイミングで⾃動設定 するかどうか確認される。
  15. 限界 - 注意点 p 操作がもっさり (できれば実機で記録するのが良い) p ⾮対応のコンポーネントがある p 確認⼿段が限定されている

    「テキスト⼀致・表⽰されているかどうか」のみ p 操作誤りをアンドゥできない p 必ず最初のActivity起動から記録される p ランドスケープ⾮対応(Assertion) 44
  16. 限界 - コンポーネント別対応状況 45 コンポーネント 対応状況 TextView ◦ EditText ◦

    Button ◦ NavigationDrawer ◦ (専⽤APIは使われない) RecyclerView △ (専⽤APIは使われない) ListView △ (onData()が使われない) Picker系 △ (spinner系のみ可) WebView内HTML ×
  17. 限界 - バグと思われるもの p ⾃動⽣成されるchildAtPosition()が、 正しく位置をカウントしない (Issue 231461) MoreViewMatchersクラス(⾃作)修正版あり p

    AsyncTask以外のバックグラウンドスレッド (OkHttpやRxJavaなど)を使っていると、 巨⼤なsleepコードが挿⼊されることがある 例: Thread.sleep(3596585)←約1時間! 46
  18. サンプルアプリ 49 (Chrome Custom Tabs) タイトル2 タイトル1 ・・・・ タイトル3 タイトル2

    Qiita記事⼀覧 お気に⼊り⼀覧 ★(CheckBox)タップで お気に⼊り⼀覧に⼊る タイトルタップで 詳細画⾯へ 記事詳細 NavigationDrawerで切り替え
  19. コンポーネント別対応状況まとめ 53 コンポーネント Recorder ⼿書き TextView ◦ ◦ EditText ◦

    ◦ Button ◦ ◦ NavigationDrawer ◦ ◦ RecyclerView △ △ ListView △ ◦ Picker系 △ ◦ WebView内HTML × ◦ より多く より 少なく
  20. その他対応状況まとめ 54 やりたいこと Recorder ⼿書き 表⽰テキスト⼀致確認 ◦ ◦ Viewの表⽰⾮表⽰確認 ◦

    ◦ その他の確認 × △ (画像⼀致確認) × × Viewプロパティ の動的な取得 × × 複雑なジェスチャー × × AsyncTask以外の⾮同 期待ち合わせ × △ より多く より 少なく 対象外
  21. サンプルアプリで考える 55 (Chrome Custom Tabs) タイトル2 タイトル1 ・・・・ タイトル3 タイトル2

    Qiita記事⼀覧 お気に⼊り⼀覧 ★(CheckBox)タップで お気に⼊り⼀覧に⼊る タイトルタップで 詳細画⾯へ 記事詳細 NavigationDrawerで切り替え 別プロセス
  22. 56 タイトル2 タイトル1 ・・・・ タイトル3 タイトル2 Qiita記事⼀覧 お気に⼊り⼀覧 ★(CheckBox)タップで お気に⼊り⼀覧に⼊る

    • 画像の変化は確認不可 Ø CheckBoxなので 「チェック状態かどうか(isChecked())」 で判定する。 サンプルアプリで考える(★のタップ操作)
  23. 57 タイトル2 タイトル1 ・・・・ タイトル3 タイトル2 Qiita記事⼀覧 お気に⼊り⼀覧 ★(CheckBox)タップで お気に⼊り⼀覧に⼊る

    • RecyclerViewのビューアイテム内の操作・ 確認が難しい Ø 汎⽤メソッドを⽤意して対応する (後述) サンプルアプリで考える(★のタップ操作)
  24. 58 タイトル2 タイトル1 ・・・・ タイトル3 タイトル2 Qiita記事⼀覧 お気に⼊り⼀覧 ★タップで お気に⼊り⼀覧に⼊る

    • お気に⼊りに⼊れたものと同じタイトル (事前に決まらない)が有ることの確認は不可 Ø サーバーレスポンスを固定する (事前に決まるようにする) Ø お気に⼊りの数が0→1になったこと への確認にとどめる(今回はこちらを採⽤) サンプルアプリで考える(お気に⼊り⼀覧)
  25. 覚えておきたいIDEのテクニック 66 コマンド Mac Windows Extract Variable Cmd+Opt+V Ctrl+Alt+V Extract

    Method Cmd+Opt+M Ctrl+Alt+M Inline Cmd+Opt+N Ctrl+Alt+N Move Line UP Ctrl+Shift+↑ Alt+Shift+↑ Duplicate Line or Selection Cmd+D Ctrl+D Generate Cmd+N Alt+Insert Help>Find Action... で⼊⼒!
  26. 修正⽅針(Recorder出⼒結果を参考に) 以下のメソッドを切り出す(int引数は位置) 71 メソッド 概要 goArticleList() 投稿⼀覧ページへ goFavsList() Fav⼀覧ページへ toggleFavStatus(int)

    Favボタンのトグル assertFavStatus (int, boolean) Favボタン状態確認 assertItemExists(int) リストアイテム存在確認 assertItemDoesntExist (int) リストアイテム不存在確認 デモ
  27. 補⾜: RxJavaの待ち合わせについて 対応⽅法は諸説ある Schedulers.io()のみ利⽤の場合は③が良さそう ① RxAndroidのIssue #149 (結論は無いがサンプルコードあり) https://github.com/ReactiveX/RxAndroid/issues/149 ②

    RxEspresso (sub/unsubをカウントする) https://github.com/stablekernel/RxEspresso ③ Retrofitting Espresso (Schedulers.io()をAsyncTaskに差し替え) https://goo.gl/2p3E6S 73