おはようございます!
セプテーニ・オリジナルの寺坂です!
GANMA!という無料マンガの配信サービスに携わっています。
GANMA!のiOSアプリではSwiftを使って開発をしているせいで、Swiftネタが多くなってますね。
かく言う今回も、Swift + UI Testingについて書きます。
さて。
Xcode7が公開されてからしばらく経ちましたが、
UIRecordingが追加されたことで、よりテストがしやすくなりました。
AndroidのEspressoだとViewのオブジェクトを取得するのが少し大変だったりしますが、
UIRecordingを利用すると、レコーディングボタンを押してシミュレーターをぽちぽち操作するだけなので、
UI Textingのぐっと敷居が下がったような気がします。
ただ、敷居が下がったとはいえ、レコーディングだけでテストできるわけではないので、
今回はUITestの基本的な書き方とViewの取得・操作方法について書こうと思います。
基本的なテストの流れ
通常は下記の流れでテストを書いていくことになると思います。
- 起動しているアプリケーション取得
- View(XCUIElement)を取得
- Viewに対してテストしたい動作を命令
- 動作結果を検証
1. アプリケーション(XCUIElement)の取得
起動中のアプリケーションを取得するのは、下記の1行で済みます。
let app = XCUIApplication()
2. エレメント(XCUIElement)取得
エレメントの取得方法はいくつかありますが、
基本的に、UIViewのaccessibilityIdentifierにセットしたIdentifierを指定して取得します。
取得方法
let button = app.buttons["identifier"]
Identifierをセットする方法
// Storyboardから設定 または コードから設定 // コードからセットする例。ViewController内で。 button.accessibilityIdentifier = "Identifier"
3. 動作を命令
用意されているメソッドを呼びます。
// タップ button.tap()
4. 動作結果を検証
Assertでチェックします。
XCTAssertTrue("hogehgoe" == "hogehoge")
XCUIElementの取得・操作方法一覧
テストをする上で、まずはエレメントを取得して操作を実行できなければテストもできません。
UIRecordingを利用すれば割と簡単に取得できますが、
応用したり独自に操作しようとすると、やはり知識が必要になってきます。
下記に、よく使われるXCUIElementの取得・操作方法を羅列していきます。 (取得方法はいろいろあるが、代表的なものを)
UILabel
// get let label = app.staticTexts["identifier"]
UIButton
// get let button = app.buttons["identifier"] // tap button.tap() // long tap button.pressForDuration(10)
UITextField
// get let textField = app.textField["identifier"] // forcus on textField.tap() // input text textField.typeText("テキスト")
UIImageView
// get let image = app.images["identifier"]
UITableViewCell
// get from title let cell = app.tables.cells.staticTexts["title"] // get from index let cell = app.tables.cells.elementBoundByIndex(4) // tap cell cell.tap()
UIAlertController
// get let alert = app.alerts["タイトル"] // check showing message alert.staticTexts["メッセージ"].exists // tap button 取り方いろいろ alert.buttons["ButtonTitle"].tap() alert.collectionViews.buttons["ButtonTitle"].tap() alert.collectionViews.cells.buttons["ButtonTitle"].tap()
UISlider
// get let slider = app.sliders["identifier"] // change slider app.sliders.element.adjustToNormalizedSliderPosition(0) app.sliders.element.adjustToNormalizedSliderPosition(0.5) app.sliders.element.adjustToNormalizedSliderPosition(1)
UIPicker操作
// wheel picker to value app.pickerWheels.element.adjustToPickerWheelValue("Hogehoge")
UIWebViewリンクタップ
// tap app.links["リンク"].tap()
UITabBarのボタン
// get app.tabBars.elementAtIndex(0)
UIToolBarのボタン
// get app.toolbars.elementAtIndex(0)
端末操作
デバイスのホームボタン操作
XCUIDevice.sharedDevice().pressButton(XCUIDeviceButton.Home)
システムアラート操作
// アラート時の動作をあらかじめ埋めておく addUIInterruptionMonitorWithDescription("Location Dialog") { (alert) -> Bool in alert.buttons["Allow"].tap() // 出た時にどうするか return true } app.buttons["Identifier"].tap() // 位置情報取得ボタン app.tap() // システムのダイアログ選択実行
キーボードキーの操作
キーボードが開いている状態で。
キーボードのEnterキー
// click Enter app.buttons["Return"].tap()
Deleteキー
// click Delete app.keys["delete"].tap()
Shiftキー
// click Shift app.keys["shift"].tap()
キーボードの切り替えボタン
// click change keyboard app.buttons["Next keyboard"].tap()
アルファベットと数字切り替え
// alphabets app.keys["more, letters"].tap() // numbers app.keys["more, numbers"].tap()
予測変換候補を選択
app.otherElements["変換内容"].tap()
その他の操作
XCUIElementの存在確認
XCTAssertTrue(エレメント.exists)
XCUIElementが表示されるまで待つ(非同期処理を挟んだ場合とかに便利)
let element = app.buttons["Identifier"] let existsPredicate = NSPredicate(format: "exists == true") expectationForPredicate(existsPredicate, evaluatedWithObject: element, handler: nil) // expectationForPredicateが完了するまで待つ waitForExpectationsWithTimeout(10, handler: nil) // handlerにはエラー時のハンドリングを書く
参考URL
WWDC https://developer.apple.com/videos/play/wwdc2015-406/
iOS9 Day-by-Day :: Day 2 :: UI Testing https://www.shinobicontrols.com/blog/ios9-day-by-day-day2-ui-testing