iOS10のリリースが間近に迫り、いろいろと新しいことにチャレンジする機会が増えそうです。iOS10がリリースされると、以下のような嬉しいことがあります。
- iOS9以上でしか使用できないXCTestの活躍の場が増える
というわけで、ここ最近UITestのコードばっかり書いてたので情報整理したい
XCTestでUITestするやり方
- 公式ドキュメント - User Interface Testing
- UITestする時、view(
XCUIElement
)を特定する方法は2つ
self.hogeButton.accessibilityIdentifier = "hogeButton";
let tablesQuery = XCUIApplication().tables
tablesQuery.cells.staticTexts["ほげほげ"].tap()
tablesQuery.cells.elementBoundByIndex(0).tap()
1.が推奨だけど、テストしたい画面のviewにいちいち指定するのが大変。
また、UIRecordingで作られるのは主に2.のようなコード。
なので、基本的に2.でテストコードを書いてみて、viewを特定するのが難しかったら、1.でやってみることにした。
UITestするとき便利なもの
UIRecording
Gather Code Coverage をオンにする
- オンにすると、Test Reportsが生成される
- Test Reportsの目玉マークを押すと、テスト中のスナップショットが閲覧できる
- テストに失敗したときどの処理で失敗したのか、とても見やすく見つけやすい
- テストを重ねると、スクショが溜まって、ディスク容量がいっぱいになるので注意
- やり方は下記のスライドの32ページからが1番わかりやすい
- http://www.slideshare.net/nowsprinting/20150711-ui-testinginxcode7
appium の inspector
- これがあると、実機でテストしたい画面を表示させて、viewの要素を階層ごとに把握できる
- セットアップや使い方は、下記の動画を見るのが1番楽だった
- https://www.youtube.com/watch?v=meU4TzI3KNM
- 一つ注意なのは、実機で inspector を表示するには下記が必要
-
brew install ideviceinstaller
Xcode の Debug View Hierarchy
- [公式ドキュメント - Examining the View Hierarchy] (https://developer.apple.com/library/ios/documentation/ToolsLanguages/Conceptual/Xcode_Overview/ExaminingtheViewHierarchy.html)
- Debug View Hierarchyで表示できない画面はviewが多すぎて、viewの特定に時間がかかり、テストに失敗することが多い(たまに成功する)
- Debug View Hierarchyで表示されない画面のUIテストは失敗率が高い(
accessibilityIdentifier
を指定しても)
先人の知恵
- http://qiita.com/i_terasaka/items/2cf7d0f7146f32c3f2c1
- http://qiita.com/shirochan/items/10271912289dc563cc36
- http://masilotti.com/ui-testing-cheat-sheet/
UITestするときに困ったことと、その解決策
cell や button をタップしてくれない時がある
-
Failed to scroll to visible (by AX action) ・・・
の場合 -
Failure fetching attributes for element ・・・
の場合 -
Xcodeのデバックエリアで
po XCUIApplication().tables.cells.elementBoundByIndex(0).buttons["ほげほげ"]
とかすれば、対象のviewは一応特定できるがテストを実行すると失敗することが多い(たまーに成功するから厄介) -
このエラーが出る画面では Debug View Hierarchy でおそらく表示できないので、viewが多すぎて
XCUIElementQuery
で探せないのが原因なのだと思う -
画面のUIを考え直すか、テストを諦めるか・・・
タップやスワイプなどのイベント動作が遅い
-
Failed to get refreshed snapshot
というエラーが出る - viewが多すぎるのが原因だけど、
UIPicker
のnumberOfRowsInComponent
で返す値が大きすぎると出る - http://stackoverflow.com/questions/35977575/failed-to-get-refreshed-snapshot-error-when-ui-testing-with-a-uipickerview
TextView や TextField にうまく文字が入力出来ない時
-
keyboardType
にUIKeyboardTypeEmailAddress
を指定しているとき、日本語だとたまにキーパットではなく、フリック入力のキーボードが立ち上がることがある - これだと
test@test.com
みたいなアドレスを入れたい時、てst@てst.com
みたいになるので困る - ダブルタップしてペーストすれば入力自体は可能
- http://stackoverflow.com/questions/32712036/xcode-ui-testing-typing-text-with-typetext-method-and-autocorrection
非同期処理などでviewが表示されるのを待ちたい
- 広告表示、APIの取得 etc で表示が遅れるとき
-
waitForExpectationsWithTimeout
を使う - http://masilotti.com/xctest-helpers/
NSTimerなどのテストで遅延したい時
- ストップウオッチがある画面などのUIをテストする時、一定の時間待ってからボタンをタップとかしたい
-
waitForExpectationsWithTimeout
とdispatch_after
の組み合わせでやる - 例えば、開始ボタンを押すと、開始ボタンが一時停止ボタンになり、一時停止を押すと再開ボタンに変わる、みたいなストップウオッチ画面の場合
ストップウオッチのボタンの移り変わり |
---|
・・・
let app = XCUIApplication()
app.buttons["開始"].tap()
// ストップウオッチのテストのために60秒遅延
let timeout:Double = 60
let dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(timeout * Double(NSEC_PER_SEC)))
dispatch_after( dispatchTime, dispatch_get_main_queue(), {
app.buttons["一時停止"].tap()
app.buttons["完了"].tap()
});
// 再開ボタンが表示されるまで(一時停止が押されるまで)待って、後続の処理を遅延
let existsPredicate = NSPredicate(format: "exists == 1")
expectationForPredicate(existsPredicate, evaluatedWithObject: app.buttons["再開"], handler: nil)
waitForExpectationsWithTimeout(timeout + 3, handler: nil)
・・・
XCTestでUITestを書いてみて
- 複雑な画面でなければ失敗することは少ない感じ
- Debug View Hierarchyで表示できない画面は、UIを考えなおす必要がありそう
- 初期コストはとても高いですが、一度書いてしまえば後がとても楽**(それがテストというもの)**
- EarlGreyなどのOSSを使うと、人によっては初期コストが少し下がるかもしれません
- appiumなら、クライアントもサーバーもテストが全てRubyとかJavaにできるので、お好みで