XCTest入門 (Swift) ~UITest編~

#はじめに
XCTestのUITestについて書きます。

XCTestのUnitTestについても以前投稿しています。見てみてください。
[XCTest入門Part1 (Swift)](http://qiita.com/okuderap/items/97f9d6026d0da59fef38)
[XCTest入門Part2 (Swift) ~非同期編~](http://qiita.com/okuderap/items/e02b73880b8bc499debe)
[XCTestでカバレッジを確認する](http://qiita.com/okuderap/items/de574685592fe0bee8ea)

#UITestって何ができる?
UITestでは、名前の通りですが主に見た目の部分をテストすることができます。

例えば、「ある操作をしたら、あるアラートが表示されること」
などを簡単に確認できます。

#UITestの流れ
XCTestでUnitTestをする場合と同様に
各テスト前に処理をするメソッドと各テスト後に処理をするメソッドがあります。

setup( )
→各テストを実行する前に毎回呼ばれます。
tearDown( )
→各テスト実行後に毎回呼ばれます。


setup( )の中身がUnitTestの場合と少し異なります。
UITestの場合は、テスト前にアプリを起動する処理が必要です。

`XCUIApplication().launch()`

また、テスト失敗時にすぐテストを止めるかどうかを決める必要があります。
（基本的には、**失敗時はすぐにテストを止める**ことが好ましいようです。）

`continueAfterFailure = false`

必要に応じて画面の向きなどもsetup( )の中で設定しておきます。

```swift:SwiftUITestSampleUITests.swift
override func setUp() {
    super.setUp()
        
    // test Failure時にすぐ止める (trueにすると、test失敗しても最後まで処理する)
    continueAfterFailure = false
        
    // セットアップ毎にアプリを起動
    XCUIApplication().launch()

    // 画面の向きなどを指定したい場合はここで設定しておく
}
```

各テスト実行後のtearDown( )は、今回は特に何も書いていません。

```swift:SwiftUITestSampleUITests.swift
override func tearDown() {
    super.tearDown()
}
```


#操作を記録する
XCTestのUITestは実際に操作した操作手順からテストコードを生成してくれる
機能があります。

右下の赤丸をクリックすると、シミュレータを起動して記録を開始します。
記録したい操作を実行しましょう。
<img width="333" alt="スクリーンショット 2016-08-20 3.07.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/103695/e2c2d336-d4de-1394-62fa-534e0ee595d1.png">


例として、
テキストフィールドをタップしてキーボードを表示し、
「123」と入力してキーボードを閉じる操作を記録しました。

生成されたコードがこちら。

```swift:SwiftUITestSampleUITests.swift
func testSample() {
        
    let app = XCUIApplication()
    let pleaseEnterHogeElementsQuery = app.otherElements.containingType(.StaticText, identifier:"Please enter \"hoge\"")
    pleaseEnterHogeElementsQuery.childrenMatchingType(.TextField).element.tap()
        
    let moreNumbersKey = app/*@START_MENU_TOKEN@*/.keys["more, numbers"]/*[[".keyboards.keys[\"more, numbers\"]",".keys[\"more, numbers\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/
    moreNumbersKey.tap()
    moreNumbersKey.tap()
    pleaseEnterHogeElementsQuery.childrenMatchingType(.TextField).element
    app/*@START_MENU_TOKEN@*/.buttons["Done"]/*[[".keyboards.buttons[\"Done\"]",".buttons[\"Done\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
    app.typeText("\n")
        
}
```

うーん、なんか違う。
コードを以下のように修正したら、記録した動作と同様の動作を再現出来ました!

```swift:SwiftUITestSampleUITests.swift
func testSample() {
        
    let app = XCUIApplication()
    let pleaseEnterHogeElementsQuery = app.otherElements.containingType(.StaticText, identifier:"Please enter \"hoge\"")
    pleaseEnterHogeElementsQuery.childrenMatchingType(.TextField).element.tap()
        
    let moreNumbersKey = app/*@START_MENU_TOKEN@*/.keys["more, numbers"]/*[[".keyboards.keys[\"more, numbers\"]",".keys[\"more, numbers\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/
    moreNumbersKey.tap()
    app.typeText("123")
    pleaseEnterHogeElementsQuery.childrenMatchingType(.TextField).element
    app/*@START_MENU_TOKEN@*/.buttons["Done"]/*[[".keyboards.buttons[\"Done\"]",".buttons[\"Done\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
}
```

期待した通りに記録されない場合がありますが、
少し手を加えるだけで簡単にUIの確認をできそうです。

#テストしてみる
サンプルとして、以下のテストをしてみたいと思います。
・Login画面でテキストフィールドに正しくない文字列を入力し、Loginボタン押下
→アラート1が表示されることを確認。

・Login画面でテキストフィールドに何も入力せず、Loginボタン押下
→アラート2が表示されることを確認。

・Login画面でテキストフィールドに正しい文字列を入力し、Loginボタン押下
→Successful画面に遷移することを確認。遷移先(Successful画面)のbackボタン押下でLogin画面に戻ってくることを確認。

【Login画面】
<img width="320" alt="Simulator Screen Shot 2016.08.20 3.55.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/103695/f28dcf2b-34cc-6973-edc9-3cecf6429af3.png">

【アラート1】
<img width="319" alt="スクリーンショット 2016-08-20 4.01.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/103695/6309462c-0a91-2af6-6dbc-d9f2dc9df553.png">

【アラート2】
<img width="320" alt="スクリーンショット 2016-08-20 4.01.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/103695/a3af241e-5027-4479-7694-e42568eac0fc.png">

【Successful画面】
<img width="320" alt="Simulator Screen Shot 2016.08.20 3.55.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/103695/f44ec54d-3b44-1be9-4417-113909c56059.png">

テストコードは、記録後修正をして最終的に以下のようになりました。

```swift:SwiftUITestSampleUITests.swift
import XCTest

class SwiftUITestSampleUITests: XCTestCase {
        
    override func setUp() {
        super.setUp()
        
        // test Failure時にすぐ止める (trueにすると、test失敗しても最後まで処理する)
        continueAfterFailure = false
        
        // セットアップ毎にアプリを起動
        XCUIApplication().launch()

        // 画面の向きなどを指定したい場合はここで設定しておく
    }
    
    override func tearDown() {
        super.tearDown()
    }
    
    func testLoginFailedCaseMistype() {
        let app = XCUIApplication()
        let pleaseEnterHogeElementsQuery = app.otherElements.containingType(.StaticText, identifier:"Please enter \"hoge\"")
        let textField = pleaseEnterHogeElementsQuery.childrenMatchingType(.TextField).element
        
        textField.tap()
        app.typeText("typo")
        
        app.buttons["Done"].tap()
        
        app.buttons["Login"].tap()
        
        sleep(1)
        
        app.alerts["ログインエラー"].collectionViews.buttons["OK"].tap()
    }
    
    func testLoginFailedCaseNotInput() {
        let app = XCUIApplication()
        let pleaseEnterHogeElementsQuery = app.otherElements.containingType(.StaticText, identifier:"Please enter \"hoge\"")
        let textField = pleaseEnterHogeElementsQuery.childrenMatchingType(.TextField).element
        
        textField.tap()
        app.typeText("")
        
        app.buttons["Done"].tap()
        
        app.buttons["Login"].tap()
        
        sleep(1)
        
        app.alerts["ログインエラー"].collectionViews.buttons["OK"].tap()
    }
    
    func testLoginSuccessful() {
        
        let app = XCUIApplication()
        let pleaseEnterHogeElementsQuery = app.otherElements.containingType(.StaticText, identifier:"Please enter \"hoge\"")
        let textField = pleaseEnterHogeElementsQuery.childrenMatchingType(.TextField).element
        
        textField.tap()
        app.typeText("hoge")
        
        let doneButton = app.buttons["Done"]
        doneButton.tap()
        
        app.buttons["Login"].tap()
        
        sleep(1)
        
        app.buttons["back"].tap()
        
        let element = app.childrenMatchingType(.Window).elementBoundByIndex(0).childrenMatchingType(.Other).elementBoundByIndex(1).childrenMatchingType(.Other).element
        let textField2 = element.childrenMatchingType(.TextField).element
        textField2.tap()
        app.typeText("Misson complete!")
        doneButton.tap()
    }
    
}

```

画面内の文言を確認するために、要所要所に `sleep(1)`を入れてみました。

#さいごに
画面遷移や文言の確認を簡単にできるようになって便利です。


Recording機能だけでは期待する動作を再現するのがなかなか難しかったので、
テストコードの書き方について整理しました。
→[XCTest入門 (Swift) ~UITest編~ Part2](http://qiita.com/okuderap/items/8875177460713bc0ecf3)