Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
XCUITestのつらさを乗り越えて、iOSアプリにUITestを導入する
Search
Sato Takeshi
September 21, 2020
Technology
9
11k
XCUITestのつらさを乗り越えて、iOSアプリにUITestを導入する
iOSDC 2020発表資料
https://fortee.jp/iosdc-japan-2020/proposal/e6c39712-3efa-4f0c-8d9d-ecebdfcae820
Sato Takeshi
September 21, 2020
Tweet
Share
More Decks by Sato Takeshi
See All by Sato Takeshi
Swift愛好会 の 思い出
satotakeshi
0
62
Xcode 15, Swift 5.9で変わる開発体験
satotakeshi
3
2.8k
Meet passkeys
satotakeshi
2
320
What's new in Vision
satotakeshi
0
1.3k
Swift Concurrency入門
satotakeshi
10
4.8k
複数端末のつらさを乗り越えてiOS UITestを実行
satotakeshi
1
360
Xcodegenを個人アプリに導入
satotakeshi
3
700
SwiftUIで作る開閉式メニュー
satotakeshi
2
2.9k
swift-snapshot-testingでVisual Testingを効率化
satotakeshi
0
1.1k
Other Decks in Technology
See All in Technology
社内で最大の技術的負債のリファクタリングに取り組んだお話し
kidooonn
1
550
Terraform CI/CD パイプラインにおける AWS CodeCommit の代替手段
hiyanger
1
240
BLADE: An Attempt to Automate Penetration Testing Using Autonomous AI Agents
bbrbbq
0
300
エンジニア人生の拡張性を高める 「探索型キャリア設計」の提案
tenshoku_draft
1
120
100 名超が参加した日経グループ横断の競技型 AWS 学習イベント「Nikkei Group AWS GameDay」の紹介/mediajaws202411
nikkei_engineer_recruiting
1
170
Why App Signing Matters for Your Android Apps - Android Bangkok Conference 2024
akexorcist
0
120
個人でもIAM Identity Centerを使おう!(アクセス管理編)
ryder472
3
200
オープンソースAIとは何か? --「オープンソースAIの定義 v1.0」詳細解説
shujisado
7
760
これまでの計測・開発・デプロイ方法全部見せます! / Findy ISUCON 2024-11-14
tohutohu
3
370
AWS Lambda のトラブルシュートをしていて思うこと
kazzpapa3
2
170
OCI Network Firewall 概要
oracle4engineer
PRO
0
4.1k
スクラム成熟度セルフチェックツールを作って得た学びとその活用法
coincheck_recruit
1
140
Featured
See All Featured
How GitHub (no longer) Works
holman
310
140k
Build The Right Thing And Hit Your Dates
maggiecrowley
33
2.4k
RailsConf 2023
tenderlove
29
900
Git: the NoSQL Database
bkeepers
PRO
427
64k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
26
2.1k
Facilitating Awesome Meetings
lara
50
6.1k
Build your cross-platform service in a week with App Engine
jlugia
229
18k
Code Review Best Practice
trishagee
64
17k
4 Signs Your Business is Dying
shpigford
180
21k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
44
2.2k
GraphQLの誤解/rethinking-graphql
sonatard
67
10k
A better future with KSS
kneath
238
17k
Transcript
XCUITestのつらさを 乗り越えて、 iOSアプリにUITestを導入する iOSDC Japan 2020 2020 年 9 月
21 日 #iosdc
Who am I • Name • 佐藤剛士(さとうたけし) • Company •
Merpay, Inc.(2019/01 ~) • Role • Software Engineer (iOS) • Account • Twitter: @hatakenokakashi • Facebook: 佐藤剛士 • GitHub: SatoTakeshiX
今日話すこと • UITestのつらさ • メルペイにUITestを導入した経緯 • XCUITest入門 • より実践的なUIテスト •
継続的に運用を続けるために
UITestのつらさ
いつの間にか壊れている
UIは変わりゆくもの-メルペイページ変遷 2019/02 〜 2019/11 〜 2020/04 〜
UITestでも CI導入が必要 壊れたテストを 素早く 発見する トリガー テスト ビルド
メルペイにUITestを導入した経緯
メルペイコードと メルカリアプリ SDK
メルカリリリースタイムライン リリース ブランチ カット App Store リリース リグレッション テスト App
Store 申請 2営業日 2営業日 1営業日
メルカリリリースタイムライン リリース ブランチ カット App Store リリース リグレッション テスト App
Store 申請 2営業日 2営業日 1営業日 修正
リグレッションテストとは? • プログラム変更に伴い、予想外の影響が現れていないかを確認す るテスト • 回帰テスト、退行テストともいう
メルカリ側ではUITestは導入済み https://engineering.mercari.com/blog/entry/2018-08-07-123000/
メルペイにUITest導入の背景 • 2019年2月にサービスイン以降、新機能開発が続く • 金融サービスを提供しているため品質は重要 • モバイルアプリのテストは手動テストのみ • QAの工数がひっ迫する状況が続く •
QA工数を減らす手段の一つとしてUITestを導入
UITestの対象 新機能をUITestするか? 既存機能をUITestするか?
UITestの対象 新機能をUITestするか? 既存機能をUITestするか? メルペイは こちらから
メルペイは既存機能をUITest化 • リグレッションテストは二週間ごとに必ず実施される • 既存機能は安定稼働されていることが望まれる • UITest化は繰り返し実施されるテストと安定稼働している機能にた いして行うと効率が良い
メルカリリリースタイムライン リリース ブランチ カット App Store リリース リグレッション テスト App
Store 申請 2営業日 2営業日 1営業日 定期的な作業を自動化で効率化!
メルペイUITestの進捗 リグレッションテスト項目 自動化対象 CircleCIパス 項目 300項目 200項目 40項目
XCUITest入門
XCUITestの重要なクラス XCUIApplication • アプリを起動/停止するプロキシ • 環境変数を指定可能 XCUIElementQuery • UI要素を検索するクエリー
• UI要素はXCUIElementとして取得 XCUIElement • 検索されたUI要素 • UI操作や状態取得が可能
テスト対象アプリ
UI要素検索 tabBars/buttonsが XCUIElementQuery
UI要素検索 「メルペイ」という タブを検索
UI要素取得 firstMatchで最初に ヒットした XCUIElementを取得
UI操作 タブをタップ メルペイ画面へ
UI要素検索 「メルペイスマート払 い残枠」ラベル検索
表示チェック ラベルが 「¥199,000」で あるか判定
Accessibility Identifier • XCUIApplicationにはUI要素を添字で検索できるXCUIElementQuery プロパティがたくさんある • staticTextsはUILabelを検索できる • Accessibility Identifierプロパティ値か、ラベルの文字列で検索がで
きる。
Accessibility Identifier
Accessibility Identifier 積極的にAccessibilityIdentifierを入れるのがオススメ
より実践的なUIテスト
Page Objectの導入
もう一つテストを作ってみよう 「利用履歴」セルを タップ
テストメソッドにUI操作を記述する弊害 複数テストで UI操作が重複
テストと画面を分ける考え方 Appiumが提唱するデザインパターン Page Object Pattern
Page Object Patternとは • 画面単位(一部も可)でクラスを定義する • UI要素やその操作メソッドを定義する • UI要素はprivateにし、外部に公開しない •
PageObjectの内部にアサーションは書かない • メソッドはPage Objectを返すとメソッドチェーンできて便利
Page Object Patternメリット 可読性の向上 • 画面とシナリオを分離 DRY原則を保つ • 同じ画面操作をまとめる 変更容易性向上
• UI要素変更 -> Page Objectのみ
Page Object実装:PageObjectableプロトコル
Page Object実装:PageObjectableプロトコル Accessibility Identifier(=A11y)を まとめる型
Page Object実装:PageObjectableプロトコル アプリを取得
Page Object実装:PageObjectableプロトコル 画面が存在するかを表す
Page Object実装:PageObjectableプロトコル ページのタイトル
Page Object実装:PageObjectableプロトコル UI要素が存在するか判定する メソッド
PageObjectableデフォルト実装 XCUIApplicationを返す
PageObjectableデフォルト実装 pageTitleが存在すれば その画面は存在する
PageObjectableデフォルト実装 引数の要素が存在すれば trueを返す
Page Object PatternでUIテスト
設定画面が表示されるかテスト
HomePage
HomePage Accessibility Identifierをまとめる (ラベル名でもOK)
HomePage UI要素をまとめる
HomePage UI操作をまとめる
MerpayPage
MerpayPage
MerpayPage Accessibility Identifierまとめる (ここではタイトルと設定セルのラ ベル名)
MerpayPage タイトルと設定画面セルの UI要素
MerpayPage 設定画面へ遷移
Merpay SettingsPage
MerpaySettingsPage
MerpaySettingsPage 画面タイトルのみ定義する
MerpaySettingsPage PageObjectableのデフォルト実装で existsプロパティから画面の存在確 認がすぐできる
テストコード
テストコード ホーム画面から メルペイ画面に行って 設定画面に行く
テストコード 設定画面が表示されているかを チェック! 設定画面 あった!
テストコード テストコード上での可読性が上がっている!
Tipsと Workaround 知っておくと便利な XCUITestのTips集 • RunActivityでテスト結果をグルー プ化 • アニメーション無効とカスタムトラ ンジション
• 同じIdentifierで違うLabelのUIを検 索する • iOS13とiOS12でUI階層が異なる 問題
RunActivityで テスト結果グループ化
Xcodeでテスト結果を確認
RunActivityでテスト結果をグループ化
RunActivityでテスト結果をグループ化 runActivity: テストログをグループ化
RunActivityでテスト結果をグループ化 テスト番号と具体的に何をするかを 名前をつける
RunActivityでテスト結果をグループ化 クロージャーに実行処理を書く
RunActivityでテスト結果をグループ化 ネストしてもOK 上から順番に呼ばれる
テスト結果の表示変化
テスト結果の表示変化 テストメソッド名
テスト結果の表示変化 1番目のrunActivity名が表示
テスト結果の表示変化 2番目のrunActivityの名前が表示 テストの結果もネストされている
テスト結果の表示変化 ログがグループ化された
アニメーション無効 と カスタムトランジション
• UITestの有名Tipsとしてアプリのアニメーション無効がある • アニメーションを無効にすることで実行時間を短くできる アニメーション無効とカスタムトランジション
アニメーション無効とカスタムトランジション • UITestの有名Tipsとしてアプリのアニメーション無効がある • アニメーションを無効にすることで実行時間を短くできる アプリ側のコード
アニメーション無効とカスタムトランジション • UITestの有名Tipsとしてアプリのアニメーション無効がある • アニメーションを無効にすることで実行時間を短くできる isUITestっていう引数があるなら アニメーション無効
アニメーション無効とカスタムトランジション • UITestの有名Tipsとしてアプリのアニメーション無効がある • アニメーションを無効にすることで実行時間を短くできる UITest側でlaunchArgumentsに 起動時に渡す引数を追加 一見有効なTipsだが、、、
アニメーション無効とカスタムトランジション • UIViewControllerTransitioningDelegateで実装した独自のトランジ ションとUIView.setAnimationsEnabled(false)の組み合わせが悪いこ とが判明 • Viewが表示されずUI操作ができなくなる
アニメーション無効とカスタムトランジション 画面に表示はない
アニメーション無効とカスタムトランジション po XCUIApplication() するとUI要素は存在している
アニメーションONでUITestを実行
同じIdentifierで 違うLabelのUIを 検索する
同じIdentifierで違うLabelのUIを検索する 「Identifier」 com.merpay.merpay_mercari_wallet_kit.wallet_history_button_view.title_label 「label」 売上金、ポイント、メルペイスマート払い
Matchingを使う
iOS13とiOS12で UI階層が異なる問題
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要素が取得できる
iOS13とiOS12でUI階層が異なる問題 • メルペイではOSバージョンごとにUITestをサポートするのがコストが 高いと判断 • UITestの対象をiOS 13以上として、場合分けの処理は実装していな い • UITestをどのOSバージョンまで対応するかはそれぞれのプロジェク
トで考慮が必要
継続的に運用を続けるために
通常のCI Build UnitTest push pull request 作成
UITestはめちゃくちゃ時間かかる テストケース:91件 並列化実行あり
適切なトリガーはなんだろう?
夜間実行 GitHub Actions Labelトリガー
夜間実行
GitHub Actions Labelトリガー
GitHub Actions Labelトリガー Pull Requestが「作成されたら」 「ラベルが追加されたら」 「追加でプッシュされたら」
GitHub Actions Labelトリガー 指定のラベルがあった場合
GitHub Actions Labelトリガー CircleCIパイプラインを実行
テスト結果をCircleCIで確認
XCTestHTMLReportを使おう • XcodeのUnit Test, UITestの 結果をHTMLに変換してくれ るツール • Xcode 11から登場したResult
Bundleも整形可能 • CircleCI上で出力された xcresultファイルを変換する https://github.com/TitouanVanBelle/XCTestHTMLReport
config.yml
config.yml store_artifacts に指定したディレクトリが アップロードされる always: エラーが起きてもアップする
config.yml fastlaneでUITestを実行
fastlaneでUITest実行
fastlaneでUITest実行 Result Bundleの出力先を指定
fastlaneでUITest実行 Result Bundleと出力先のディレクトリ を指定
fastlaneでUITest実行 Result BundleをHTMLファイルに 変換
テスト結果がCircleCI上で確認できる
まとめ
まとめ • UITestにもCIを導入して失敗を検知するようにしよう • メルペイではQA効率化の一環でUITestを導入した • XCUITestによるUITestの実装の流れ • 実践的なUITestの実装方法やTips •
UITestで継続的な運用をする方法
参考文献 • Appleの公式ドキュメント「Testing Your Apps in Xcode」の紹介 • iOSアプリ開発自動テストの教科書 •
SeleniumHQ/selenium Page Objects • Swift での UI テストの雑なまとめ • https://github.com/TitouanVanBelle/XCTestHTMLReport
おまけ
システムアラート操作
システムアラート操作 位置情報許可アラートを操作!
システムアラート操作 XCUIApplication()では システムアラートの要素が 取得できない
システムアラート操作 bundleIdentifierに"com.apple.springboard" でシステムのUI要素を取得する