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

XCUITestのつらさを乗り越えて、iOSアプリにUITestを導入する

Sato Takeshi
September 21, 2020

 XCUITestのつらさを乗り越えて、iOSアプリにUITestを導入する

Sato Takeshi

September 21, 2020
Tweet

More Decks by Sato Takeshi

Other Decks in Technology

Transcript

  1. XCUITestのつらさを

    乗り越えて、

    iOSアプリにUITestを導入する

    iOSDC Japan 2020

    2020 年 9 月 21 日 

    #iosdc


    View Slide

  2. Who am I

    ● Name

    ● 佐藤剛士(さとうたけし)

    ● Company

    ● Merpay, Inc.(2019/01 ~)

    ● Role

    ● Software Engineer (iOS)

    ● Account

    ● Twitter: @hatakenokakashi

    ● Facebook: 佐藤剛士

    ● GitHub: SatoTakeshiX


    View Slide

  3. 今日話すこと

    ● UITestのつらさ

    ● メルペイにUITestを導入した経緯

    ● XCUITest入門

    ● より実践的なUIテスト

    ● 継続的に運用を続けるために


    View Slide

  4. UITestのつらさ


    View Slide

  5. いつの間にか壊れている


    View Slide

  6. UIは変わりゆくもの-メルペイページ変遷

    2019/02

    2019/11

    2020/04

    View Slide

  7. UITestでも

    CI導入が必要

    壊れたテストを

    素早く

    発見する

    トリガー
    テスト
    ビルド

    View Slide

  8. メルペイにUITestを導入した経緯


    View Slide

  9. メルペイコードと

    メルカリアプリ
 SDK

    View Slide

  10. メルカリリリースタイムライン

    リリース

    ブランチ

    カット

    App Store

    リリース

    リグレッション

    テスト

    App Store

    申請

    2営業日
 2営業日

    1営業日


    View Slide

  11. メルカリリリースタイムライン

    リリース

    ブランチ

    カット

    App Store

    リリース

    リグレッション

    テスト

    App Store

    申請

    2営業日
 2営業日

    1営業日

    修正


    View Slide

  12. リグレッションテストとは?

    ● プログラム変更に伴い、予想外の影響が現れていないかを確認す
    るテスト

    ● 回帰テスト、退行テストともいう


    View Slide

  13. メルカリ側ではUITestは導入済み

    https://engineering.mercari.com/blog/entry/2018-08-07-123000/

    View Slide

  14. メルペイにUITest導入の背景

    ● 2019年2月にサービスイン以降、新機能開発が続く

    ● 金融サービスを提供しているため品質は重要

    ● モバイルアプリのテストは手動テストのみ

    ● QAの工数がひっ迫する状況が続く

    ● QA工数を減らす手段の一つとしてUITestを導入


    View Slide

  15. UITestの対象

    新機能をUITestするか? 既存機能をUITestするか?

    View Slide

  16. UITestの対象

    新機能をUITestするか? 既存機能をUITestするか?
    メルペイは

    こちらから


    View Slide

  17. メルペイは既存機能をUITest化

    ● リグレッションテストは二週間ごとに必ず実施される

    ● 既存機能は安定稼働されていることが望まれる

    ● UITest化は繰り返し実施されるテストと安定稼働している機能にた
    いして行うと効率が良い


    View Slide

  18. メルカリリリースタイムライン

    リリース

    ブランチ

    カット

    App Store

    リリース

    リグレッション

    テスト

    App Store

    申請

    2営業日
 2営業日

    1営業日

    定期的な作業を自動化で効率化!


    View Slide

  19. メルペイUITestの進捗

    リグレッションテスト項目 

    自動化対象

    CircleCIパス

    項目

    300項目

    200項目

    40項目


    View Slide

  20. XCUITest入門


    View Slide

  21. XCUITestの重要なクラス

    XCUIApplication

    ● アプリを起動/停止するプロキシ

    ● 環境変数を指定可能

    XCUIElementQuery

    ● UI要素を検索するクエリー 

    ● UI要素はXCUIElementとして取得

    XCUIElement

    ● 検索されたUI要素 

    ● UI操作や状態取得が可能 


    View Slide

  22. テスト対象アプリ


    View Slide

  23. UI要素検索

    tabBars/buttonsが
    XCUIElementQuery

    View Slide

  24. UI要素検索

    「メルペイ」という
    タブを検索

    View Slide

  25. UI要素取得

    firstMatchで最初に
    ヒットした
    XCUIElementを取得

    View Slide

  26. UI操作

    タブをタップ
    メルペイ画面へ

    View Slide

  27. UI要素検索

    「メルペイスマート払
    い残枠」ラベル検索

    View Slide

  28. 表示チェック

    ラベルが
    「¥199,000」で
    あるか判定

    View Slide

  29. Accessibility Identifier

    ● XCUIApplicationにはUI要素を添字で検索できるXCUIElementQuery
    プロパティがたくさんある

    ● staticTextsはUILabelを検索できる

    ● Accessibility Identifierプロパティ値か、ラベルの文字列で検索がで
    きる。


    View Slide

  30. Accessibility Identifier


    View Slide

  31. Accessibility Identifier

    積極的にAccessibilityIdentifierを入れるのがオススメ


    View Slide

  32. より実践的なUIテスト


    View Slide

  33. Page Objectの導入


    View Slide

  34. もう一つテストを作ってみよう

    「利用履歴」セルを
    タップ

    View Slide

  35. テストメソッドにUI操作を記述する弊害

    複数テストで
    UI操作が重複

    View Slide

  36. テストと画面を分ける考え方

    Appiumが提唱するデザインパターン

    Page Object Pattern


    View Slide

  37. Page Object Patternとは

    ● 画面単位(一部も可)でクラスを定義する

    ● UI要素やその操作メソッドを定義する

    ● UI要素はprivateにし、外部に公開しない

    ● PageObjectの内部にアサーションは書かない

    ● メソッドはPage Objectを返すとメソッドチェーンできて便利


    View Slide

  38. Page Object Patternメリット

    可読性の向上
 ● 画面とシナリオを分離

    DRY原則を保つ
 ● 同じ画面操作をまとめる

    変更容易性向上
 ● UI要素変更 -> Page Objectのみ


    View Slide

  39. Page Object実装:PageObjectableプロトコル


    View Slide

  40. Page Object実装:PageObjectableプロトコル

    Accessibility Identifier(=A11y)を
    まとめる型

    View Slide

  41. Page Object実装:PageObjectableプロトコル

    アプリを取得

    View Slide

  42. Page Object実装:PageObjectableプロトコル

    画面が存在するかを表す

    View Slide

  43. Page Object実装:PageObjectableプロトコル

    ページのタイトル

    View Slide

  44. Page Object実装:PageObjectableプロトコル

    UI要素が存在するか判定する
    メソッド

    View Slide

  45. PageObjectableデフォルト実装

    XCUIApplicationを返す

    View Slide

  46. PageObjectableデフォルト実装

    pageTitleが存在すれば
    その画面は存在する

    View Slide

  47. PageObjectableデフォルト実装

    引数の要素が存在すれば
    trueを返す

    View Slide

  48. Page Object PatternでUIテスト


    View Slide

  49. 設定画面が表示されるかテスト


    View Slide

  50. HomePage


    View Slide

  51. HomePage

    Accessibility Identifierをまとめる
    (ラベル名でもOK)

    View Slide

  52. HomePage

    UI要素をまとめる

    View Slide

  53. HomePage

    UI操作をまとめる

    View Slide

  54. MerpayPage


    View Slide

  55. MerpayPage


    View Slide

  56. MerpayPage

    Accessibility Identifierまとめる
    (ここではタイトルと設定セルのラ
    ベル名)

    View Slide

  57. MerpayPage

    タイトルと設定画面セルの
    UI要素

    View Slide

  58. MerpayPage

    設定画面へ遷移

    View Slide

  59. Merpay

    SettingsPage


    View Slide

  60. MerpaySettingsPage


    View Slide

  61. MerpaySettingsPage
 画面タイトルのみ定義する

    View Slide

  62. MerpaySettingsPage

    PageObjectableのデフォルト実装で
    existsプロパティから画面の存在確
    認がすぐできる

    View Slide

  63. テストコード


    View Slide

  64. テストコード

    ホーム画面から
    メルペイ画面に行って
    設定画面に行く

    View Slide

  65. テストコード

    設定画面が表示されているかを
    チェック!
    設定画面
    あった!

    View Slide

  66. テストコード

    テストコード上での可読性が上がっている!

    View Slide

  67. Tipsと
    Workaround

    知っておくと便利な

    XCUITestのTips集

    ● RunActivityでテスト結果をグルー
    プ化

    ● アニメーション無効とカスタムトラ
    ンジション

    ● 同じIdentifierで違うLabelのUIを検
    索する

    ● iOS13とiOS12でUI階層が異なる
    問題


    View Slide

  68. RunActivityで

    テスト結果グループ化


    View Slide

  69. Xcodeでテスト結果を確認


    View Slide

  70. RunActivityでテスト結果をグループ化


    View Slide

  71. RunActivityでテスト結果をグループ化

    runActivity: テストログをグループ化

    View Slide

  72. RunActivityでテスト結果をグループ化

    テスト番号と具体的に何をするかを
    名前をつける

    View Slide

  73. RunActivityでテスト結果をグループ化

    クロージャーに実行処理を書く

    View Slide

  74. RunActivityでテスト結果をグループ化

    ネストしてもOK
    上から順番に呼ばれる

    View Slide

  75. テスト結果の表示変化


    View Slide

  76. テスト結果の表示変化

    テストメソッド名

    View Slide

  77. テスト結果の表示変化

    1番目のrunActivity名が表示

    View Slide

  78. テスト結果の表示変化

    2番目のrunActivityの名前が表示
    テストの結果もネストされている

    View Slide

  79. テスト結果の表示変化

    ログがグループ化された

    View Slide

  80. アニメーション無効

    と

    カスタムトランジション


    View Slide

  81. ● UITestの有名Tipsとしてアプリのアニメーション無効がある

    ● アニメーションを無効にすることで実行時間を短くできる

    アニメーション無効とカスタムトランジション



    View Slide

  82. アニメーション無効とカスタムトランジション



    ● UITestの有名Tipsとしてアプリのアニメーション無効がある

    ● アニメーションを無効にすることで実行時間を短くできる

    アプリ側のコード

    View Slide

  83. アニメーション無効とカスタムトランジション



    ● UITestの有名Tipsとしてアプリのアニメーション無効がある

    ● アニメーションを無効にすることで実行時間を短くできる

    isUITestっていう引数があるなら
    アニメーション無効

    View Slide

  84. アニメーション無効とカスタムトランジション



    ● UITestの有名Tipsとしてアプリのアニメーション無効がある

    ● アニメーションを無効にすることで実行時間を短くできる

    UITest側でlaunchArgumentsに
    起動時に渡す引数を追加
    一見有効なTipsだが、、、


    View Slide

  85. アニメーション無効とカスタムトランジション



    ● UIViewControllerTransitioningDelegateで実装した独自のトランジ
    ションとUIView.setAnimationsEnabled(false)の組み合わせが悪いこ
    とが判明

    ● Viewが表示されずUI操作ができなくなる


    View Slide

  86. アニメーション無効とカスタムトランジション



    画面に表示はない

    View Slide

  87. アニメーション無効とカスタムトランジション



    po XCUIApplication()
    するとUI要素は存在している

    View Slide

  88. アニメーションONでUITestを実行



    View Slide

  89. 同じIdentifierで

    違うLabelのUIを

    検索する


    View Slide

  90. 同じIdentifierで違うLabelのUIを検索する



    「Identifier」

    com.merpay.merpay_mercari_wallet_kit.wallet_history_button_view.title_label 

    「label」

    売上金、ポイント、メルペイスマート払い 


    View Slide

  91. Matchingを使う



    View Slide

  92. iOS13とiOS12で

    UI階層が異なる問題


    View Slide

  93. iOS13とiOS12でUI階層が異なる問題



    iOS 12
    Button, 0x6000024e3100, {{203.0, 475.5}, {145.0, 29.0}}, label: '振込申請とスケジュール ’
    iOS 13
    Button, 0x600000754700, {{203.0, 475.5}, {145.0, 29.0}}, label: '振込申請とスケジュール '
    StaticText, 0x6000007547e0, {{203.0, 481.5}, {145.0, 17.0}}, label: '振込申請とスケジュール '
    iOS 13にはStaticText要素が取得できる

    View Slide

  94. iOS13とiOS12でUI階層が異なる問題

    ● メルペイではOSバージョンごとにUITestをサポートするのがコストが
    高いと判断

    ● UITestの対象をiOS 13以上として、場合分けの処理は実装していな
    い

    ● UITestをどのOSバージョンまで対応するかはそれぞれのプロジェク
    トで考慮が必要


    View Slide

  95. 継続的に運用を続けるために


    View Slide

  96. 通常のCI

    Build
    UnitTest
    push
 pull

    request

    作成


    View Slide

  97. UITestはめちゃくちゃ時間かかる

    テストケース:91件

    並列化実行あり


    View Slide

  98. 適切なトリガーはなんだろう?


    View Slide

  99. 夜間実行
 GitHub Actions

    Labelトリガー


    View Slide

  100. 夜間実行


    View Slide

  101. GitHub Actions Labelトリガー


    View Slide

  102. GitHub Actions Labelトリガー

    Pull Requestが「作成されたら」
    「ラベルが追加されたら」
    「追加でプッシュされたら」

    View Slide

  103. GitHub Actions Labelトリガー

    指定のラベルがあった場合

    View Slide

  104. GitHub Actions Labelトリガー

    CircleCIパイプラインを実行

    View Slide

  105. テスト結果をCircleCIで確認


    View Slide

  106. XCTestHTMLReportを使おう

    ● XcodeのUnit Test, UITestの
    結果をHTMLに変換してくれ
    るツール

    ● Xcode 11から登場したResult
    Bundleも整形可能

    ● CircleCI上で出力された
    xcresultファイルを変換する

     

    https://github.com/TitouanVanBelle/XCTestHTMLReport

    View Slide

  107. config.yml


    View Slide

  108. config.yml

    store_artifacts
    に指定したディレクトリが
    アップロードされる
    always: エラーが起きてもアップする

    View Slide

  109. config.yml

    fastlaneでUITestを実行

    View Slide

  110. fastlaneでUITest実行


    View Slide

  111. fastlaneでUITest実行

    Result Bundleの出力先を指定

    View Slide

  112. fastlaneでUITest実行

    Result Bundleと出力先のディレクトリ
    を指定

    View Slide

  113. fastlaneでUITest実行

    Result BundleをHTMLファイルに
    変換

    View Slide

  114. テスト結果がCircleCI上で確認できる


    View Slide

  115. まとめ


    View Slide

  116. まとめ

    ● UITestにもCIを導入して失敗を検知するようにしよう

    ● メルペイではQA効率化の一環でUITestを導入した

    ● XCUITestによるUITestの実装の流れ

    ● 実践的なUITestの実装方法やTips

    ● UITestで継続的な運用をする方法


    View Slide

  117. 参考文献

    ● Appleの公式ドキュメント「Testing Your Apps in Xcode」の紹介

    ● iOSアプリ開発自動テストの教科書

    ● SeleniumHQ/selenium Page Objects

    ● Swift での UI テストの雑なまとめ

    ● https://github.com/TitouanVanBelle/XCTestHTMLReport


    View Slide

  118. おまけ


    View Slide

  119. システムアラート操作


    View Slide

  120. システムアラート操作



    位置情報許可アラートを操作!

    View Slide

  121. システムアラート操作



    XCUIApplication()では
    システムアラートの要素が
    取得できない

    View Slide

  122. システムアラート操作



    bundleIdentifierに"com.apple.springboard"
    でシステムのUI要素を取得する

    View Slide