138
129

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Xcode7から導入されたXCTestのUI recordingで遊んでみる

Last updated at Posted at 2015-06-13

はじめに

2015年6月のWWDC2015でiOS9とかSwift 2.0とかが発表されましたが、
それらを扱うためのXcodeの新バージョンであるXcode7も発表されました。
Xcode7の新機能の中でも、個人的にはUI recordingが特に気になったので、簡単に触ってみました。

最初に言っておくと、
「XcodeでSelenium IDEのように画面操作を記録しつつ、Selenium WebDriverのようにテストコードを記述・実行できる」
という内容です。

テスト対象のアプリを作る

使用感を確かめるために、まずはテストアプリとしてボタンを押すと数字がカウントアップするアプリを作りました。
(2020-03-10 追記: Swift5にMigrationしました)

「+1」ボタンをタップすると、その回数が表示されます。
スクリーンショット 2015-06-13 17.16.21.png

UIの動作をテストする

アプリができたので、早速テストを作ってみましょう。

Test Navigatorのタブでテストを選ぶ

test_navigator.png
Test Navigatorのタブでは、作成したテストケースがツリー上に表示されます。
(これはXcodeプロジェクト作成時に「Include Unit Tests」「Include UI Tests」を選択すると自動生成されるテストケースです)

今回はCountUpUITests.TestExampleに対してテストコードを書いていきます。

UI上の操作を記録する

下の図のように「UIの操作を行うコード」を挿入したい位置にカーソルを合わせた状態で、
録画ボタンを押すと、アプリケーションが立ち上がり記録可能状態になります。
(既にアプリケーションが立ち上がっている場合、その状態から記録が始まります)
record_button.png

では早速「+1」のボタンをタップしてみると…
click_1.png
こんな風に、

XCUIApplication().buttons["+ 1"].tap()

というコードが挿入されました。
もう一度タップしてみると、↓のようなコードが生成されます。

let 1Button = XCUIApplication().buttons["+ 1"]
1Button.tap()
1Button.tap()

若干イイ感じにリファクタリングしてくれました。
(が、文法的にNGな変数が作られるので、手動で修正する必要があります。
Xcodeが自動でリファクタリングするのに、その結果に対してXcodeがエラー吐くとか…)

あと、UILabelをタップしたときのイベントも記録できます。

app.staticTexts["2"] //操作時のラベルのテキストの内容がキーとして扱われる

ラベルテキストをキーとして用いるのはアレなので、
実運用では、他の方法(後述)で対象オブジェクトを同定して扱うことになると思います。

記録を終了する

記録を終了するには、録画ボタンを再度押せばOKです。
(*記録中は、記録により生成されたコードをいじることはできないようです)

記録したテストの実行

記録ができたので、Assertionを入れて実際にテストを動かしてみます。

Assertionの追加

せっかくのテストなので、テキトーにAssertionを入れておきます。

ボタンを2回タップしたときのラベル表示を検証するテスト
func testExample() {
    let app = XCUIApplication()
    let staticText = app.staticTexts.elementAtIndex(0)
    let button = app.buttons["+ 1"] //"+1"ボタン

    XCTAssertTrue(staticText.label == "0") //起動時のラベルは"0"
    button.tap()
    button.tap()
    XCTAssertTrue(staticText.label == "2") //ボタンを2回タップしたので、ラベルは"2"になっているはず
}

staticTextの取得方法のところで、app.staticTexts.elementAtIndex(0)としています。
これによって、ラベルのテキスト内容にかかわらず0番目のラベルを取得できます。
("さっきよりマシ"程度の取得方法です)

UIテストの実行

というわけで、テストを実行してみましょう!
(テストケースのLine number左側に「◇」アイコンがあるので、それをクリックすればテストケースが実行されます)

実際にテストを実行した動画がこちら。
ui_test.png
Xcode7 UI Testing
(動画では見えていませんが、テスト終了後に「Test Succeeded」のポップアップが出ています)

こんな風に、UIの自動テストができちゃいます!
既存のiOS向けサードパーティ製のUIテストフレームワークはあまり触ったことがないのでちゃんとした比較はできませんが、
まぁ大体Selenium(WebDriver)と似たような感じで使えるのかなーと。
何より、一つのツールでコーディング・デバッグ・単体テスト・UIテストすべてを実行できるのは非常に助かります。

UIオブジェクトをもっと賢く取得する

さきほどまでは、テスト対象のオブジェクトを取得する方法がかなりテキトーだったので、
CSSのid指定のように対象オブジェクトを狙い撃ちで取得できるように修正を入れます。

やること

テスト時に取得したいオブジェクトについて、Identifierを指定するだけです。
StoryBoard(IB)・コードそれぞれで指定が可能です。

StoryBoard(Interface Builder)を用いる方法

対象のオブジェクトを選択してIdentity Inspectorを開いた後、
AccessibilityグループのIdentifierに、ユニークな文字列を指定します。
identifer.png

コードに直接記述する方法

対象のオブジェクトのaccessibilityIdentifierにユニークな文字列を指定します。

countLabel.accessibilityIdentifier = "testLabel"

テストコードを書き直す

Identifierを指定したら、それをキーにしてオブジェクトを取得できるように修正します。

func testExample() {
    let app = XCUIApplication()
    let staticText = app.staticTexts["testLabel"]
    let button = app.buttons["testButton"]

    XCTAssertTrue(staticText.label == "0") //起動時のラベルは"0"
    button.tap()
    button.tap()
    XCTAssertTrue(staticText.label == "2") //ボタンを2回タップしたので、ラベルは"2"になっているはず
}

ここまで来ると、だいぶキレイな感じになりますね。

補足

最初からIdentifierを指定しておけば、記録時にちゃんとIdentifierを認識してコードを生成してくれます。
また、その際は変数名が「Identifier+ElementType」となるようで、
上述したような「自動リファクタリングでエラーになる」という謎な状態にはなりません。

Identifierを指定している状態で記録を行った場合
let testbuttonButton = app.buttons["testButton"]
testbuttonButton.tap()
testbuttonButton.tap()

おわりに

Xcode7から導入されたUI recordingの機能を使って、簡単にUIテストを作ってみました。
今回はUIButtonとUILabelのみを使用しましたが、XCUIElementTypeの定義を見ると多様なオブジェクトを扱えるようです。
また、オブジェクトの取得についてもXCUIElementQueryに定義されているメソッドを見ると様々な方法で行えるようです。

現時点ではこれらのクラスのリファレンスやサンプルコードはAppleから提供されていないみたいなのでほぼ完全に手探りになりますが、それなりに感覚的に利用できるのでそこまで困ることはなさそうです。

138
129
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
138
129

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?