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

Espressoの知識ゼロでも書ける!Android UIテストはじめの一歩 / The First Step of Android UI Testing

TOYAMA Sumio
December 12, 2019

Espressoの知識ゼロでも書ける!Android UIテストはじめの一歩 / The First Step of Android UI Testing

GDG DevFest Tokyo 2019のハンズオンセッション「Espressoの知識ゼロでも書ける!Android UIテストはじめの一歩」の資料(スライドパート)です。

TOYAMA Sumio

December 12, 2019
Tweet

More Decks by TOYAMA Sumio

Other Decks in Programming

Transcript

  1. ⓒ 2019 DeNA Co., Ltd.
    2019/12/14
    システム本部 品質統括部 品質管理部
    SWETグループ
    DeNA Co., Ltd.
    1
    Espressoの知識ゼロでも書ける!

    Android UIテスト はじめの一歩

    View Slide

  2. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    講師紹介
    ● 氏名: 外山 純生 (TOYAMA Sumio)
    @sumio_tym (Twitter) / @sumio (GitHub)
    ● 所属: DeNA SWETグループ
    (Software Engineer in Test)
    ● 業務内容:
    − Androidアプリ開発 テスト自動化支援
    ● その他:
    − 「Androidテスト全書」執筆
    − DroidKaigi 2020登壇予定
    2

    View Slide

  3. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    チューター紹介
    3
    ● 氏名: 田熊 希羽 (Nozomi Takuma)
    @fgfgtkm (Twitter) / @tkmnzm (GitHub)
    ● 所属: DeNA SWETグループ
    (Software Engineer in Test)
    ● 外山と同じくAndroidアプリ開発のテスト自動化支援
    をしています
    ● ハンズオン中につまづいたりわからないことがあれ
    ば、お気軽にどうぞ!

    View Slide

  4. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    この資料について
    本資料は、関係者の許諾の元で
    書籍「Androidテスト全書」の「4章 UIテスト(概要編)」
    の内容を一部流用して作成しています。
    「Androidテスト全書」にはここで紹介する内容のほか、
    役立つ情報が満載です。
    気になる方は読んでみてください!
    https://peaks.cc/android_testing
    4

    View Slide

  5. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    本ハンズオンの目標
    ● UIテストの特徴を理解する
    ● 長くテストコードを利用し続けるために
    必要なPage Objectデザインパターンを
    理解する
    ● Espressoを使ったUIテストを書き始める
    ことができる
    ※Espresso APIの説明はありません
    5

    View Slide

  6. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    本資料の構成
    本日のハンズオンでは一部割愛します
    1. UIテストの自動化を始める前に
    2. テストツール選択のポイント
    3. 長くテストコードを利用し続けるには
    4. Page Objectデザインパターンを使ってテストを
    書いてみよう
    6

    View Slide

  7. ⓒ 2019 DeNA Co., Ltd.
    1. UIテストの自動化を始める前に
    7
    1. UIテストの特徴
    2. 目的を整理する
    3. 自動化する範囲を決める
    4. テストツールを選択する
    5. スケジュール・予算・体制を決める

    View Slide

  8. ⓒ 2019 DeNA Co., Ltd.
    1. UIテストの自動化を始める前に
    8
    1. UIテストの特徴
    2. 目的を整理する
    3. 自動化する範囲を決める
    4. テストツールを選択する
    5. スケジュール・予算・体制を決める

    View Slide

  9. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-1. UIテストの特徴①
    ユニットテスト vs UIテスト
    9
    This image is reproduced from work created and shared by the Android Open Source Project and used according to terms
    described in the Creative Commons 2.5 Attribution License.

    View Slide

  10. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-1. UIテストの特徴①
    ユニットテスト vs UIテスト
    10
    This image is reproduced from work created and shared by the Android Open Source Project and used according to terms
    described in the Creative Commons 2.5 Attribution License.
    UIテストはユニットテストに比べて・・
    ● より実環境に近い(Fidelityが高い)
    ● 実行時間が長い
    ● メンテナンスコストが高い
    ● デバッグコストが高い

    View Slide

  11. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-1. UIテストの特徴②
    (参考) UIテストの分類
    11
    忠実度 実行時間 分類
    高 遅い E2E Test
    中 普通 Instrumented Test
    低 速い Local Test (Robolectric)

    View Slide

  12. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-1. UIテストの特徴③
    ● 画面デザインが変更されると、テストコードも修
    正しなければならない
    ● UIに起因する理由でテストが安定しない
    − UI操作→画面変化までの時間がまちまち
    − 操作を中断するダイアログが突然表示されるかも
    − ログイン画面
    − パーミッションダイアログ
    12
    無計画に自動化すると
    運用コストの増加に耐えられなくなることに!

    View Slide

  13. 13
    せっかく自動化したUIテストの運用を
    諦めてしまわないために・・・
    きちんと計画をたてましょう。
    ● 目的を整理する
    ● 自動化する範囲を決める
    ● テストツールを選択する
    ● スケジュール・予算・体制を決める

    View Slide

  14. ⓒ 2019 DeNA Co., Ltd.
    1. UIテストの自動化を始める前に
    14
    1. UIテストの特徴
    2. 目的を整理する
    3. 自動化する範囲を決める
    4. テストツールを選択する
    5. スケジュール・予算・体制を決める

    View Slide

  15. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-2. 目的を整理する①
    テスト自動化の利害関係者は意外とたくさん
    ● プロダクトオーナー
    ● QAチーム
    ● 開発チーム
    ● etc.
    15
    UIテスト自動化への期待は人それぞれ!

    View Slide

  16. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-2. 目的を整理する②
    ● テスト担当者への精神的負担の軽減
    「退屈な(MPを消費する)テストから解放されたい」
    ● 開発コストの削減
    「リリースまでのリードタイム短縮したい」
    ● 既存機能のバグを予防
    「思わぬバグを入れてしまう不安から解放されたい」
    16
    UIテスト自動化への期待を関係者で議論する
    「検証コストの削減」以外にも・・・

    View Slide

  17. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-2. 目的を整理する③
    条件を追加すると達成しやすい
    ● 検証コスト削減 + 精神的負担の軽減
    ● 検証コスト削減 + 既存機能のバグを予防
    17
    UIテスト自動化で一番難しいこと
    ● 自動化にかかる投資を
    検証コストの減少だけで回収すること
    意思決定者と合意しておくのを忘れない!

    View Slide

  18. ⓒ 2019 DeNA Co., Ltd.
    1. UIテストの自動化を始める前に
    18
    1. UIテストの特徴
    2. 目的を整理する
    3. 自動化する範囲を決める
    4. テストツールを選択する
    5. スケジュール・予算・体制を決める

    View Slide

  19. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-3. 自動化する範囲を決める①
    ● 自動化できるものは全て自動化する
    ● 目的から逸れた箇所を自動化して満足してし
    まう
    19
    自動化の範囲をむやみに広げると
    メンテナンスコストが上がってしまう!
    UIテスト自動化のアンチパターン

    View Slide

  20. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-3. 自動化する範囲を決める②
    1. 手動・自動関係なく、どのような切り口でテスト
    をするか整理する
    2. 目的を達成できる自動化候補を選ぶ
    − 検証コスト削減が目的なら・・・
    何度も行うテスト・時間がかかるテスト
    − QAの精神的負担軽減が目的なら・・・
    担当者が辛いと思っているテスト
    20

    View Slide

  21. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-3. 自動化する範囲を決める③
    3. 自動化候補に優先度をつける
    − 自動化が容易なものを優先する
    採用するテストツールによっても変化する
    − 変更可能性が少ない画面を優先する
    − 自動操作する画面数が少なくなるようにする
    自動操作するUIコンポーネント数が増えると辛い
    (テスト書く工数が増える)
    4. 予算内に収まるように自動化する範囲を決める
    21

    View Slide

  22. ⓒ 2019 DeNA Co., Ltd.
    1. UIテストの自動化を始める前に
    22
    1. UIテストの特徴
    2. 目的を整理する
    3. 自動化する範囲を決める
    4. テストツールを選択する
    5. スケジュール・予算・体制を決める

    View Slide

  23. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-4. テストツール(*)を選択する①
    次の観点でテストツールを選ぶ
    ● 自動化したい内容が簡単に実現できるか
    − トラブル無く、安定してテスト実行できるか
    ● 低コストで学習できるか
    − 担当者にとって書きやすいか
    − 近くに質問できる人がいるか
    ● 開発コミュニティは活発か
    − 今後も開発が続きそうか?
    − 情報が充実しているか?
    23
    (*) ここではUIテスト自動化を実現するツールのことを指す

    View Slide

  24. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-4. テストツールを選択する②
    自動化が不可能・困難なものは
    どのツールを選択しても無理なことに注意
    ● 端末に対する物理的な操作
    − 端末を傾ける、振る、など
    ● Viewと無関係な場所の操作
    − 指で文字を描く、など
    24

    View Slide

  25. ⓒ 2019 DeNA Co., Ltd.
    1. UIテストの自動化を始める前に
    25
    1. UIテストの特徴
    2. 目的を整理する
    3. 自動化する範囲を決める
    4. テストツールを選択する
    5. スケジュール・予算・体制を決める

    View Slide

  26. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-5. スケジュール・予算・体制を決める
    それぞれポイントに絞って紹介します
    1. スケジュール
    − とにかくスモールスタートで始める
    − リリース優先度を考慮したスケジュールにする
    2. 予算
    − ROIを意識する
    − 目標を決めておく
    3. 体制
    − テスト自動化担当者を確保する
    26

    View Slide

  27. ⓒ 2019 DeNA Co., Ltd.
    1-5-1. スケジュール検討のポイント
    27

    View Slide

  28. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-5-1. スケジュール検討のポイント①
    とにかくスモールスタートで始めよう
    ● UIテストでは運用フェーズで直面する課題が多い
    ● 運用期間をできるだけ長く確保するのが大事
    ❌ 全部作ってからCIで運用を開始!
    一つだけ作ったらCIで運用を開始!
    28

    View Slide

  29. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-5-1. スケジュール検討のポイント②
    リリース優先度を考慮したスケジュールにしよう
    (新機能のテストを自動化する場合)
    ● リリース優先度が高いテストから自動化する
    ● 自動化が間に合わなければQAに検証依頼
    ● 自動テストの結果レポートを元にリリース内容を調整
    29

    View Slide

  30. ⓒ 2019 DeNA Co., Ltd.
    1-5-2. 予算検討のポイント
    30

    View Slide

  31. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-5-2. 予算検討のポイント①
    ROI (Return On Investment)を意識する
    ● 自動化にかけたコストに対する利益の割合
    ● 1を超えれば投資を回収できたことになる
    ● 自動テストを繰り返すとROIが増えることが多い
    31
    テスト自動化の利益
    テスト自動化の初期コスト + テスト自動化後の運用コスト
    ROI =

    View Slide

  32. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-5-2. 予算検討のポイント②
    ROIの計算: 自動化にかかるコスト
    (: 時間の経過ともに増えていくもの)
    ● 初期コスト
    − テストツールの学習コスト
    − テストを書くコスト
    ● 運用コスト
    − テスト結果レポートの解析・原因切り分けコスト
    − プロダクト仕様変更にともなうテスト修正コスト
    32

    View Slide

  33. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-5-2. 予算検討のポイント③
    ROIの計算: テスト自動化の利益
    以下の項目の和を計算する
    33
    ➕/

    項目
    ➕ 手動のままテストを続けたときの運用コスト
    ➖ 自動化にかかる初期コスト
    ➖ テストを自動化した後の運用コスト

    View Slide

  34. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-5-2. 予算検討のポイント④
    ROIの定義をもう一度
    34
    テスト自動化の利益
    テスト自動化の初期コスト + テスト自動化後の運用コスト
    ROI =
    ● 通常は テスト自動化の利益>テスト自動化後の運用コスト
    ● 運用が長くなると(自動テストを繰り返すと)
    ROIが1を超えることが多い

    View Slide

  35. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-5-2. 予算検討のポイント⑤
    目標を決めておく
    ● 改善策の検討や継続可否の判断のために必要
    − ROIが1を超える(損益分岐点に達する)時期
    (それまでに必要な自動テスト実施回数)
    − 検証コスト削減以外が目的の場合は・・・
    どれだけコストをかけ続けられるか
    35

    View Slide

  36. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-5-2. 予算検討のポイント⑥
    目標を決めたいけど見積もりが難しいとき
    ● 初期開発コストは頑張って見積もる
    − 1ケースだけ作成し、その実績を元に見積もる
    − 過去の事例を参考にする
    − 経験者に聞く
    ● 自動化の運用コストはざっくり決める
    (例: 2人時/週)
    − 後から精度を上げていけばOK
    36

    View Slide

  37. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-5-2. 予算検討のポイント⑦
    目標の達成度を知るために
    運用時に作業時間を記録すること!
    ● ROIを計算するために必要
    ● 記録しないと、自動化が成功しているかどうか判断で
    きなくなるので重要!
    37

    View Slide

  38. ⓒ 2019 DeNA Co., Ltd.
    1-5-3. 体制検討のポイント
    38

    View Slide

  39. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    1-5-3. 体制検討のポイント
    テスト自動化担当者を確保する
    ● 専任のテスト自動化担当者を設けるのは難しい
    − テストを自動化できる=ソフトウェアを開発できる
    − プロダクトの開発で忙しいことが多い
    ● 兼任の場合は、以下を必ず確保する
    − 自動化に必要な学習・実装の時間
    − 有識者に相談できるルート
    (片手間で自動化するのは無理)
    39

    View Slide

  40. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ここまでのまとめ
    ● 関係者でUIテストの目的を決めましょう
    − 検証コスト削減以外の目的もあり
    ● 目的を達成できるように以下を決めていく
    − 自動化する範囲
    − 目的の範囲内で容易に実現できるものを優先
    − 採用するテストツール
    − スケジュール・予算・体制
    − スモールスタートで早く運用に入る
    − ROIを意識する
    − 自動化のための時間を確保できる体制にする
    40

    View Slide

  41. ⓒ 2019 DeNA Co., Ltd.
    2. テストツール選択のポイント
    41
    1. EspressoとAppiumの概要
    2. EspressoとAppiumの比較

    View Slide

  42. ⓒ 2019 DeNA Co., Ltd.
    2. テストツール選択のポイント
    42
    1. EspressoとAppiumの概要
    2. EspressoとAppiumの比較

    View Slide

  43. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    2-1. EspressoとAppiumの概要①
    Espresso
    ● AndroidX Testの一部として公式に提供
    ● 実機・エミュレータで動くInstrumented Test
    (Robolectricでも動くようになってきた)
    ● APIレベル19以上であれば、
    UI Automatorと併用して他アプリの操作も可能
    43

    View Slide

  44. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    2-1. EspressoとAppiumの概要②
    Appium
    ● 3rd party OSS(開発: JS Foundation)
    ● リリース版apkをそのままテストできる
    (E2E Testに向いている)
    ● Selenium WebDriverと同様なAPIで
    Androidアプリをテストする
    ● クライアント/サーバアーキテクチャ
    ● 内部はEspressoやUI Automatorを使って実現
    44

    View Slide

  45. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    2-1. EspressoとAppiumの概要③
    (参考)Appiumのアーキテクチャ
    45
    Appium
    クライアントラ
    イブラリ
    スクリプト
    Appium
    サーバ
    テスト対象
    アプリ
    HTTP
    (Mobile JSON Wire Protocol)
    EspressoやUI Automator
    を使って操作
    クライアントPC サーバーPC スマートフォン
    USB

    View Slide

  46. ⓒ 2019 DeNA Co., Ltd.
    2. テストツール選択のポイント
    46
    1. EspressoとAppiumの概要
    2. EspressoとAppiumの比較

    View Slide

  47. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    2-2. EspressoとAppiumの比較①
    Appiumも内部でEspressoを使うようになった
    (Appium Espresso WebDriver)ので、
    できることの違いはほとんど無くなった
    (違いの例)
    ● Espresso: RecyclerView専用API
    ● Appium: OpenCVによる画像検索API
    47

    View Slide

  48. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    2-2. EspressoとAppiumの比較②
    48
    Espresso Appium
    テスト実行速度 速い 遅い
    プログラミング言

    Java・Kotlinのみ Ruby・JavaScript・Ja
    va・Kotlinなど
    テストランナーの
    実行場所
    Android端末内部(*) 通常のPC
    環境構築 簡単 難しい
    対応DeviceFarm 多い 少ない
    (*)Robolectricを使うことでlocal JVMでも実行可能

    View Slide

  49. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    2-2. EspressoとAppiumの比較③
    UIテストは実行速度が命!
    以下の事情がなければEspressoを選ぶと良い
    ● 既にAppiumによる自動化ノウハウがある
    ● テスト自動化担当者がAndroidエンジニアではない
    ● Androidアプリ以外の操作を含むテストシナリオを自動
    化したい
    「PCブラウザで会員登録してから
    アプリでログインする」など
    49

    View Slide

  50. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    2-2. EspressoとAppiumの比較④
    (参考) Appiumを選択する場合
    パフォーマンス向上策の参考記事
    ● Appium Pro #50 (Android向け)
    Special Capabilities for Speeding up Android Test Initialization
    https://appiumpro.com/editions/50
    ● Appium Pro #77 (iOS向け)
    Optimizing WebDriverAgent Startup Performance
    https://appiumpro.com/editions/77
    50

    View Slide

  51. ⓒ 2019 DeNA Co., Ltd.
    3. 長くテストコードを利用
    し続けるには
    51
    1. Page Objectデザインパターンを適用する
    2. CI環境でテストコードを動かし続ける
    3. 検証結果をわかりやすく共有する
    4. 不安定なテストを対策する
    5. 実行時間を短縮させる
    6. 開発・リリースフローに組み込む

    View Slide

  52. ⓒ 2019 DeNA Co., Ltd.
    3. 長くテストコードを利用
    し続けるには
    52
    1. Page Objectデザインパターンを適用する
    2. CI環境でテストコードを動かし続ける
    3. テスト結果をわかりやすく共有する
    4. 不安定なテストを対策する
    5. 実行時間を短縮させる
    6. 開発・リリースフローに組み込む

    View Slide

  53. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    3-1. Page Objectデザインパターンを適用する
    ● 画面を1つのオブジェクトとして定義するデザインパ
    ターン
    ● テストコードを共通化する指針
    ● 対象アプリのUI が変更されたときのテストコード修正
    コストを小さくすることを目的としている
    53

    View Slide

  54. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    テストコードの共通化: テスト対象
    54
    ユーザーID
    パスワード
    ログイン
    invalid_user
    **********
    ログイン
    ログインできません
    ログイン失敗
    ようこそ!
    ログイン成功

    View Slide

  55. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    55
    @Test
    fun login_success() {
    //ユーザー名とパスワードを入力してログイン
    onView(withHint("ユーザーID")).perform(replaceText("valid_user"))
    onView(withHint("パスワード")).perform(replaceText("valid_password"))
    onView(withText("ログイン")).perform(click())
    //ログインが成功して「ようこそ」と表示されている
    onView(withId(R.id.welcomeMessage)).check(matches(withText("ようこそ! ")))
    }
    @Test
    fun login_error() {
    // 不正なユーザー名とパスワードを入力してログインボタンをクリック
    onView(withHint("ユーザーID")).perform(replaceText("invalid_user"))
    onView(withHint("パスワード")).perform(replaceText("invalid_password"))
    onView(withText("ログイン")).perform(click())
    // ログインエラーのメッセージが表示されている
    onView(withId(R.id.errorMessage)).check(matches(withText("ログインできません")))
    }
    テストコードの共通化: 共通化前

    View Slide

  56. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    テストコードの共通化: 共通化前
    56
    @Test
    fun login_success() {
    //ユーザー名とパスワードを入力してログイン
    onView(withHint("ユーザーID")).perform(replaceText("valid_user"))
    onView(withHint("パスワード")).perform(replaceText("valid_password"))
    onView(withText("ログイン")).perform(click())
    //ログインが成功して「ようこそ」と表示されている
    onView(withId(R.id.welcomeMessage)).check(matches(withText("ようこそ! ")))
    }
    @Test
    fun login_error() {
    // 不正なユーザー名とパスワードを入力してログインボタンをクリック
    onView(withHint("ユーザーID")).perform(replaceText("invalid_user"))
    onView(withHint("パスワード")).perform(replaceText("invalid_password"))
    onView(withText("ログイン")).perform(click())
    // ログインエラーのメッセージが表示されている
    onView(withId(R.id.errorMessage)).check(matches(withText("ログインできません")))
    }
    重複が多い

    View Slide

  57. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    テストコードの共通化: 共通化前
    57
    @Test
    fun login_success() {
    //ユーザー名とパスワードを入力してログイン
    onView(withHint("ユーザーID")).perform(replaceText("valid_user"))
    onView(withHint("パスワード")).perform(replaceText("valid_password"))
    onView(withText("ログイン")).perform(click())
    //ログインが成功して「ようこそ」と表示されている
    onView(withId(R.id.welcomeMessage)).check(matches(withText("ようこそ! ")))
    }
    @Test
    fun login_error() {
    // 不正なユーザー名とパスワードを入力してログインボタンをクリック
    onView(withHint("ユーザーID")).perform(replaceText("invalid_user"))
    onView(withHint("パスワード")).perform(replaceText("invalid_password"))
    onView(withText("ログイン")).perform(click())
    // ログインエラーのメッセージが表示されている
    onView(withId(R.id.errorMessage)).check(matches(withText("ログインできません")))
    }
    ボタン名が「ログインする」に
    変更されたとき、2箇所修正が必要

    View Slide

  58. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    テストコードの共通化: ログイン操作を共通化
    58
    @Test
    fun login_success() {
    onView(withHint("ユーザーID")).perform(replaceText("valid_user"))
    onView(withHint("パスワード")).perform(replaceText("valid_password"))
    onView(withText("ログイン")).perform(click())
    onView(withId(R.id.welcomeMessage)).check(matches(withText("ようこそ! ")))
    }
    fun login(id: String, password: String) {
    onView(withHint("ユーザーID")).perform(replaceText(id))
    onView(withHint("パスワード")).perform(replaceText(password))
    onView(withText("ログイン")).perform(click())
    }
    ログイン操作を
    抽出

    View Slide

  59. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    59
    @Test
    fun login_success() {
    login("valid_user", "valid_password")
    onView(withId(R.id.welcomeMessage)).check(matches(withText("ようこそ! ")))
    }
    @Test
    fun login_error() {
    login("invalid_user", "invalid_password")
    onView(withId(R.id.errorMessage)).check(matches(withText("ログインできません")))
    }
    private fun login(id: String, password: String) {
    onView(withHint("ユーザーID")).perform(replaceText(id))
    onView(withHint("パスワード")).perform(replaceText(password))
    onView(withText("ログイン")).perform(click())
    }
    テストコードの共通化: 共通化後

    View Slide

  60. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    60
    @Test
    fun login_success() {
    login("valid_user", "valid_password")
    onView(withId(R.id.welcomeMessage)).check(matches(withText("ようこそ! ")))
    }
    @Test
    fun login_error() {
    login("invalid_user", "invalid_password")
    onView(withId(R.id.errorMessage)).check(matches(withText("ログインできません")))
    }
    private fun login(id: String, password: String) {
    onView(withHint("ユーザーID")).perform(replaceText(id))
    onView(withHint("パスワード")).perform(replaceText(password))
    onView(withText("ログイン")).perform(click())
    }
    テストコードの共通化: 共通化後
    ボタン名が「ログインする」に
    変更されたときの修正はここのみ

    View Slide

  61. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    Page Objectデザインパターンの考え方
    1. テスト対象の画面ごとにクラス(Page クラス)を定義する
    2. Page クラスにその画面が提供するサービスをメソッドとして定
    義する
    a. アクションを実行するメソッド・ページ情報を取得するメソッドなど
    3. アクションを実行するメソッドは戻り値として、
    遷移先の画面に対応するPageオブジェクトを返す
    4. テストコードではPageクラスのメソッドのみを使う
    61

    View Slide

  62. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    Page Objectのサービス
    62
    ● 画面が提供するサービスとは何か?に着目し、メソッ
    ドとして定義する
    − 画面の詳細や構造はPage Objectの中に隠蔽
    ● 例: ログイン画面
    fun login(id, password)
    LoginPage
    fun inputID(id)
    fun inputPass(password)
    fun clickLogin()
    LoginPage

    View Slide

  63. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ログイン画面のテストにあてはめる
    1. loginメソッドをLoginPageクラスに移動する
    2. 画面に表示されるメッセージを検証するコードもそれぞれの
    Pageクラスに移動する
    a. ログイン成功時のメッセージ検証: MainPage
    b. ログイン失敗時のメッセージ検証: LoginPage
    3. 各テストメソッドではPageクラスに定義されたメソッドのみ利用
    する
    63

    View Slide

  64. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ログイン画面のテストにあてはめる
    64
    test_login_success()
    test_login_failure()
    LoginTest
    login_success(id, password): MainPage
    login_failure(id, password): LoginPage
    assertErrorMessage(message): LoginPage
    LoginPage
    assertWelcomeMessage(message): MainPage
    MainPage
    利用する

    View Slide

  65. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ログイン画面のテストにあてはめる
    65
    test_login_success()
    test_login_failure()
    LoginTest
    login_success(id, password): MainPage
    login_failure(id, password): LoginPage
    assertErrorMessage(message): LoginPage
    LoginPage
    assertWelcomeMessage(message): MainPage
    MainPage
    利用する
    画面ごとにPageクラスを
    定義する

    View Slide

  66. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    test_login_success()
    test_login_failure()
    LoginTest
    ログイン画面のテストにあてはめる
    66
    login_success(id, password): MainPage
    login_failure(id, password): LoginPage
    assertErrorMessage(message): LoginPage
    LoginPage
    assertWelcomeMessage(message): MainPage
    MainPage
    利用する
    戻り値は遷移先の画面の
    Pageクラスにする

    View Slide

  67. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ログイン画面のテストにあてはめる
    67
    test_login_success()
    test_login_failure()
    LoginTest
    login_success(id, password): MainPage
    login_failure(id, password): LoginPage
    assertErrorMessage(message): LoginPage
    LoginPage
    assertWelcomeMessage(message): MainPage
    MainPage
    利用する
    テストコードはPageクラスの
    メソッドのみ用いて実装する

    View Slide

  68. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    68
    class LoginPage {
    fun loginSuccess(id: String, password: String): MainPage {
    login(id, password)
    return MainPage()
    }
    fun loginFailure(id: String, password: String): LoginPage {
    login(id, password)
    return this
    }
    private fun login(id: String, password: String) {
    onView(withHint("ユーザーID")).perform(replaceText(id))
    onView(withHint("パスワード")).perform(replaceText(password))
    onView(withText("ログイン")).perform(click())
    }
    ..
    ログインページクラスの実装: アクション

    View Slide

  69. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ログインページクラスの実装: アクション
    69
    class LoginPage {
    fun loginSuccess(id: String, password: String): MainPage{
    login(id, password)
    return MainPage()
    }
    fun loginFailure(id: String, password: String): LoginPage {
    login(id, password)
    return this
    }
    fun login(id: String, password: String) {
    onView(withHint("ユーザーID")).perform(replaceText(id))
    onView(withHint("パスワード")).perform(replaceText(password))
    onView(withText("ログイン")).perform(click())
    }
    ..
     画面のアクションをメソッドとして実装

    View Slide

  70. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    class LoginPage {
    fun loginSuccess(id: String, password: String): MainPage {
    login(id, password)
    return MainPage()
    }
    fun loginFailure(id: String, password: String): LoginPage {
    login(id, password)
    return this
    }
    ..
    遷移をせず画面にとどまる場合は
    thisを返す
    ログインページクラスの実装: アクション
    70
    戻り値はアクションをした後の
    遷移先画面のPageクラス

    View Slide

  71. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ログインページクラスの実装: アクション
    71
    class LoginPage {
    fun loginSuccess(id: String, password: String): MainPage{
    login(id, password)
    return MainPage()
    }
    fun loginFailure(id: String, password: String): LoginPage {
    login(id, password)
    return this
    }
    private fun login(id: String, password: String) {
    onView(withHint("ユーザーID")).perform(replaceText(id))
    onView(withHint("パスワード")).perform(replaceText(password))
    onView(withText("ログイン")).perform(click())
    }
    ..
     各アクションで使われるログイン処理の
    共通メソッド

    View Slide

  72. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    72
    class LoginPage {
    ..
    fun assertErrorMessage(message: String): LoginPage {
    onView(withId(R.id.errorMessage))
    .check(matches(withText(message)))
    return this
    }
    ..
    }
    ログインページクラスの実装: アサーション
    ログイン失敗時の
    エラーメッセージ検証メソッド

    View Slide

  73. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    Pageクラスを用いたログインのテストコード
    73
    class LoginTest {
    @Test
    fun login_error() {
    LoginPage()
    .loginFailure("invalid_user", "invalid_password")
    .assertErrorMessage("ログインできません")
    }
    ..
    }
    テストコードはPageクラスの
    メソッドのみ用いて実装する

    View Slide

  74. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    Page Objectを実装するときのポイント
    74
    ● Pageクラスの外には画面UIの詳細を露出させない(レ
    イアウト構造など)
    ● 画面が提供するサービスをすべてPageクラスに実装
    する必要はない
    ● 同じアクションでも遷移先が異なる場合は
    それぞれ別のメソッドにする

    View Slide

  75. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    Page Object参考リンク
    75
    ● SeleniumのPage Object解説
    − https://github.com/SeleniumHQ/selenium/wiki/PageObjects
    − assertをテストケースで実装するなど、
    Espressoを使っている場合は実現できない原則もある
    ● Martinfowler.com
    − https://martinfowler.com/bliki/PageObject.html

    View Slide

  76. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    Page Objectデザインパターンのおさらい
    ● 画面を1つのオブジェクトとして定義するデザインパ
    ターン
    ● テストコードを共通化するときの指針であり、テスト
    コード修正コストを小さくすることが目的
    ● Page Objectを適用させるときはこの目的を意識しな
    がら実装をする
    76

    View Slide

  77. ⓒ 2019 DeNA Co., Ltd.
    3. 長くテストコードを利用
    し続けるには
    77
    1. Page Objectデザインパターンを適用する
    2. CI環境でテストコードを動かし続ける
    3. 検証結果をわかりやすく共有する
    4. 不安定なテストを対策する
    5. 実行時間を短縮させる
    6. 開発・リリースフローに組み込む

    View Slide

  78. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    3-2. CI環境でテストコードを動かし続ける
    ● CI 環境でUIテストを継続的に実行する
    − 自身以外の環境でもテストコードが動くことを確認する
    − 定期的な実行により、テストコードの安定性を確かめる
    − CI環境で実行することで運用に乗せやすくする
    ● UIテストはユニットテストと比較すると、実行環境や外
    部要因の影響をうけやすく壊れやすい
    ● CI環境で継続して動かすことで安定性を確かめる
    78

    View Slide

  79. ⓒ 2019 DeNA Co., Ltd.
    3. 長くテストコードを利用
    し続けるには
    79
    1. Page Objectデザインパターンを適用する
    2. CI環境でテストコードを動かし続ける
    3. テスト結果をわかりやすく共有する
    4. 不安定なテストを対策する
    5. 実行時間を短縮させる
    6. 開発・リリースフローに組み込む

    View Slide

  80. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    3-3. テスト結果をわかりやすく共有する
    80
    ● レポート形式でテスト結果を出力する
    ● Slackにテストの成功と失敗を通知する
    ● テスト失敗の原因がわかりやすいようにする
    − 失敗時のスクリーンショット・各種ログ・動画を保存する
    − 理由がわかりやすくなっていないと調査コストがかかる
    ● 直近の結果だけでなく今までの履歴も保存する
    − どのテストケースが不安定になっているかを確認できる

    View Slide

  81. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    3-3. テスト結果をわかりやすく共有する
    81
    ● Allure: 多機能なテストレポート表示ツール
    − http://allure.qatools.ru/
    − JUnit互換のテスト結果XMLがあれば表示できる
    − Android向けadapterもあり(発展途上)
    https://github.com/TinkoffCreditSystems/allure-android
    # インストール
    $ brew install allure
    # テスト実行
    $ ./gradlew connectedAndroidTest
    # テスト結果レポートをAllureで表示
    $ allure serve \
    app/build/outputs/androidTest-results/connected/

    View Slide

  82. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    3-3. テスト結果をわかりやすく共有する
    82
    ● Allure Adapter
    − テスト結果レポートの内容をカスタマイズできる
    テスト失敗時にスクリーンショットを自動で追加
    − Android Instrumented Test向けは発展途上
    https://github.com/TinkoffCreditSystems/allure-android

    View Slide

  83. ⓒ 2019 DeNA Co., Ltd.
    3. 長くテストコードを利用
    し続けるには
    83
    1. Page Objectデザインパターンを適用する
    2. CI環境でテストコードを動かし続ける
    3. テスト結果をわかりやすく共有する
    4. 不安定なテストを対策する
    5. 実行時間を短縮させる
    6. 開発・リリースフローに組み込む

    View Slide

  84. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    3-4. 不安定なテストの対策をする
    ● たまに失敗する不安定なテストを放置せずに
    対処を決める
    ● 失敗が放置されている状況が続くと、テストの信頼性
    もなくなってしまう
    ● 不安定なテストを捨てるという選択肢も検討する
    84

    View Slide

  85. ⓒ 2019 DeNA Co., Ltd.
    3. 長くテストコードを利用
    し続けるには
    85
    1. Page Objectデザインパターンを適用する
    2. CI環境でテストコードを動かし続ける
    3. テスト結果をわかりやすく共有する
    4. 不安定なテストを対策する
    5. 実行時間を短縮させる
    6. 開発・リリースフローに組み込む

    View Slide

  86. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    3-5. 実行時間を短縮させる
    86
    ● UIテストは実行時間が課題
    − 自動テストが落ちたときの対応が面倒になる
    − 自動テストの完了が待てない
    − 実行に1時間以上かかるケースもある
    − その結果失敗しても放置される、使われなくなる...
    ● 不必要なテストケースを作らない
    − テストピラミッドで推奨されるUIテストの割合は10%
    − UIテストとして追加が必要かを考える
    ● テストを並列実行する

    View Slide

  87. ⓒ 2019 DeNA Co., Ltd.
    3. 長くテストコードを利用
    し続けるには
    87
    1. Page Objectデザインパターンを適用する
    2. CI環境でテストコードを動かし続ける
    3. テスト結果をわかりやすく共有する
    4. 不安定なテストを対策する
    5. 実行時間を短縮させる
    6. 開発・リリースフローに組み込む

    View Slide

  88. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    3-6. 開発やリリースフローに組み込む
    88
    ● 実装したUIテストは実際に運用され、開発フローで利
    用されることで意味のあるものになる
    ● 開発・リリース時のフローに組み込むことで、
    定期的にUIテストが実行される環境を作る
    − PR時に実行される
    − テストがパスしていないとPRのマージができない
    − 自動テストがすべてパスしていることがリリースの条件
    etc...

    View Slide

  89. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ここまでのまとめ
    1. Page Objectデザインパターンを適用する
    − テストコードを共通化して変更に強くしよう
    2. CI環境でテストコードを動かし続ける
    3. テスト結果もわかりやすく共有する
    − テスト失敗の原因がすぐわかるようにしよう
    4. 不安定なテストを対策する
    5. 実行時間を短縮させる
    6. 開発・リリースフローに組込む
    89

    View Slide

  90. ⓒ 2019 DeNA Co., Ltd.
    90
    codelabで説明します!
    https://dena.github.io/codelabs/android-ui-tests-basic/
    4. Page Objectデザインパターンを
    使ってテストを書いてみよう

    View Slide

  91. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    (付録) ショートカット一覧 Mac版
    91
    Action名 ショートカット 意味
    Move Line Down Opt+Shift+↓ 選択範囲を下に移動する
    Change Signature Cmd+F6 メソッドシグネチャを変更する
    Move F6 (メソッドなどを)
    別クラスや別ファイルに移動する
    Extract Function To
    Scope
    Opt+Shift+Cmd+M 選択範囲をメソッドとして抽出する
    Extract Parameter Opt+Cmd+P 選択箇所をメソッド引数にする
    Show Intention
    Actions
    Opt+Enter (空気を読んで)
    必要なアクションを提案する
    Help>Find Action… で入力する名前
    ショートカットを忘れたときに便利!

    View Slide

  92. ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    ⓒ 2019 DeNA Co., Ltd.
    (付録) ショートカット一覧 Windows版
    92
    Action名 ショートカット 意味
    Move Line Down Alt+Shift+↓ 選択範囲を下に移動する
    Change Signature Ctrl+F6 メソッドシグネチャを変更する
    Move F6 (メソッドなどを)
    別クラスや別ファイルに移動する
    Extract Function To
    Scope
    Ctrl+Alt+Shift+M 選択範囲をメソッドとして抽出する
    Extract Parameter Ctrl+Alt+P 選択箇所をメソッド引数にする
    Show Intention
    Actions
    Alt+Enter (空気を読んで)
    必要なアクションを提案する
    Help>Find Action… で入力する名前
    ショートカットを忘れたときに便利!

    View Slide