こんにちは、iOSアプリの開発を担当しているOTTサービス技術部開発第3Gの鈴木です。
皆様、UIテストは実施しておりますでしょうか。
UIテストを実施する場面としては、リグレッションテストが多いかと思います。 そんなUIテストが自動化されているかというと、まだまだ人力で頑張っているような状況が多いかと思います。 自動化できるテストはどんどん自動化していきたいですね。
ということで、今回はiOSアプリ開発におけるUIテスト自動化ツールとして候補に上がりがちな、「XCUITest」と、「Maestro」の二つを比較します。
本記事が皆様のUIテストの導入、及びその自動化の手助けになれば幸いです。
XCUITest とは?
XCUITestとは、Appleが提供するUIテストフレームワークの一つで、iOSおよびmacOSアプリケーションのUIを自動でテストするためのツールです。 テストスクリプトはSwiftで記述します。 Xcodeと統合されており、Xcode内でテストを作成、実行、デバッグできます。
Maestro とは?
Maestroは、モバイルアプリケーションのUIテストを簡単かつ効率的に行うためのオープンソースのフレームワークです。 テストスクリプトはYAML形式で記述します。 iOSとAndroidの両方のプラットフォームをサポートしているので、同一のUIであれば、ほぼ同じ記述でテストできるところは嬉しいです。 オープンソースとして無料で利用できるところも嬉しいですね。
比較してみる
今回はサンプルアプリとして、GitHub APIを利用したRepository検索アプリを用意しました。 こちらを使用し、XCUITestとMaestroを比較していきます。
テスト内容
本テストで行うUI操作は以下になります。
- アプリを起動する
- 画面上部の検索バーを押下する
- 「Swift」と入力する
- 検索ボタンを押下する
- 「Swift」のRepositoryを選択する(詳細画面に遷移する)
- 「Back」ボタンを押下する(検索画面に戻る)
- 「Cancel」ボタンを押下する(検索文字・検索結果を全て削除し、「リポジトリが見つかりませんでした」の表示となる)
また、本テストでの確認項目内容は以下になります。
- 検索後、検索結果が画面に表示されていることを確認する
- 「Swift」のRepositoryを選択後、詳細画面に遷移したことを確認する
- 詳細画面で「Swift」のRepositoryの情報を表示していることを確認する
- 「Back」ボタンを押下後、検索画面に遷移したことを確認する
- 「Cancel」ボタンを押下後、検索結果が画面に表示されていないことを確認する
XCUITestの場合
XCUITestの場合は以下のような記述になります。
final class UITestSampleUITests: XCTestCase { private var app: XCUIApplication! override func setUpWithError() throws { try super.setUpWithError() continueAfterFailure = false app = XCUIApplication() // ACTION: ① アプリを起動 app.launch() } override func tearDownWithError() throws { // Put teardown code here. This method is called after the invocation of each test method in the class. } func test_searchSwiftRepository() throws { let navBar = app.navigationBars["_TtGC7SwiftUI19UIHosting"] let searchBar = navBar.searchFields["search"] // ACTION: ② 検索バー押下 searchBar.tap() // ACTION: ③ 「Swift」と入力する searchBar.typeText("Swift") // ACTION: ④ 検索ボタンを押下 app.buttons["search"].tap() // CHECK: ① 検索結果が画面に表示されていることを確認する XCTAssert(app.buttons["RepositoryCell"].firstMatch.waitForExistence(timeout: 5)) // ACTION: ⑤ 「Swift」のRepositoryを選択する app.collectionViews.staticTexts["swiftlang"].tap() // CHECK: ② 詳細画面に遷移したことを確認する XCTAssert(app.otherElements["RepositoryDetailView"].exists) // CHECK: ③ 詳細画面で「Swift」のRepositoryの情報を表示していることを確認する XCTAssert(app.staticTexts["The Swift Programming Language"].firstMatch.exists) // ACTION: ⑥「Back」ボタンを押下する navBar.buttons["Back"].tap() // CHECK: ④ 検索画面に遷移したことを確認する XCTAssert(app.otherElements["RepositoriesView"].exists) // ACTION: ⑦「Cancel」ボタンを押下する navBar.buttons["Cancel"].tap() // CHECK: ⑤ 検索結果が画面に表示されていないことを確認する XCTAssertFalse(app.buttons["RepositoryCell"].exists) } }
Maestroの場合
Maestroの場合は以下のような記述になります。
appId: jp.suzuki.play.githubRepositorySearch --- # ACTION: ① アプリを起動 - launchApp # ACTION: ② 検索バー押下 - tapOn: "search" # ACTION: ③ 「Swift」と入力する - inputText: Swift # ACTION: ④ 検索ボタンを押下 - tapOn: id: "Search" # CHECK: ① 検索結果が画面に表示されていることを確認する - assertVisible: id: "RepositoryCell" # ACTION: ⑤ 「Swift」のRepositoryを選択する - tapOn: "swiftlang" # CHECK: ② 詳細画面に遷移したことを確認する - assertVisible: id: "RepositoryDetailView" # CHECK: ③ 詳細画面で「Swift」のRepositoryの情報を表示していることを確認する - assertVisible: "The Swift Programming Language" # ACTION: ⑥「Back」ボタンを押下する - tapOn: "Back" # CHECK: ④ 検索画面に遷移したことを確認する - assertVisible: id: "RepositoriesView" # ACTION: ⑦「Cancel」ボタンを押下する - tapOn: "Cancel" # CHECK: ⑤ 検索結果が画面に表示されていないことを確認する - assertNotVisible: id: "RepositoryCell"
違いについて
各ツール間の違いについては、以下の三点を感じました。
- Wait処理の記述有無
- 実行環境
- 複雑なUIへの対応
まず、Wait処理の記述有無についてです。
XCUITestでは、待機が必要な場合はwaitForExistence(timeout: 5)
のように待機処理を記述する必要がありますが、Maestroでは記述不要なので、テストスクリプトが簡潔になります。
次に実行環境についてです。 Maestroの実行環境はSimulatorのみに限られます。 一方、XCUITestはSimulator・実機の両方で実行可能なため、実機で確認したいUIをテスト可能なことは、XCUITestの強みだと思います。
最後に複雑なUIへの対応についてです。 今回のテストではそこまで感じませんでしたが、業務でMaestroを利用している中で、 ネストが深いような複雑な作りの画面だと、MaestroにてUIパーツを判別できずテストが難しくなるようなことがありました。 複雑な画面のUITestを行う場合は、XCUITestで行うことをオススメします。
Wait処理の記述有無 | 実行環境 | 複雑なUIへの対応 | |
---|---|---|---|
XCUITest | 必要 | Simulator or 実機 | 対応可能 |
Maestro | 不要 | SImulator | UIパーツの識別が困難な場合あり |
まとめ
今回、「XCUITest」と「Maestro」を比較してみて、それぞれの魅力や便利さを実感することができました。 是非、これらのツールを活用してUIテストの自動化の推進を図ってみてはいかがでしょうか。