XCTest入門 (Swift) ~UITest編~ Part2

#はじめに
UITest編Part2です。

前回は、UIRecordingを使ってシミュレータでの操作からテストコードを生成しました。
[XCTest入門 (Swift) ~UITest編~](http://qiita.com/okuderap/items/b6211cc3524ad2385150)


前回、UIRecordingを使って操作を記録しましたが
生成されたテストコードが期待通りの動作をしてくれず修正が必要でした。
そのため、今回はコードの書き方を中心にまとめたいと思います。

(前回使用したサンプルプロジェクトをそのまま流用しています。)

#テストコードからUIを呼び出す
UIを指定する方法はいくつかありますが、私が実際に使用したものを記載します。
###方法1. StoryboardでAccessbilityのIdentifierを設定して呼び出す
<img width="572" alt="スクリーンショット 2016-08-23 0.55.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/103695/c5f062fa-fa8c-8491-887e-c53acdae0c54.png">

本サンプルでは、Loginボタンに対して"loginButton"というIdentifierを設定しました。

テストコードで、

```swift
XCUIApplication().buttons["loginButton"]
```
というように指定することができます。

```swift
XCUIApplication().buttons["loginButton"].tap()
```
とすれば、ログインボタンをタップする動作を表現できますね。

###方法2. 同じUIが複数存在しない場合はelementのみで呼び出す
表示されている画面に同じUIが複数存在しなければ、elementと書くだけで呼び出すことができます。

上のログイン画面はボタンが1つしか存在しないので、以下のように書くとログインボタンが呼び出されます。

```swift
XCUIApplication().buttons.element
```
ボタンではなく、テキストフィールドであれば

```swift
XCUIApplication().textFields.element
```

という感じです。

###方法3. titleを指定する
Identifierでは無く、表示されるタイトルでもUIを指定することができます。
書き方は、Identifierを指定する場合と同様です。

```swift
XCUIApplication().buttons["Login"]
```

私は、
方法1(Identifier指定)と
方法2(同UIが複数存在しない場合のelementでの呼び出し)
を主に使用しています。

#操作
操作についても私が実際に使用したものを記載します。
今後、新しく使用したら追記していきたいです。
###タップ
xxx.tap()でタップを表現できます。

```swift
XCUIApplication().buttons["Done"].tap()
XCUIApplication().textFields["loginTextField"].tap()
```

###ロングタップ
xxx.pressForDuration(<秒数>)

```swift
XCUIApplication().textFields["loginTextField"].pressForDuration(1.0)
```

###キーボードで入力
TextFieldなどに対して、xxx.typeText(<入力文字列>)

```swift
XCUIApplication().textFields["loginTextField"].typeText("abc")
```

###Pasteboardに文字列をセットしてペースト
事前にペーストボードに文字列をセットしておいて、
"Paste"をタップしてTextFieldにペーストする方法です。
<img width="414" alt="Simulator Screen Shot 2016.08.23 1.33.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/103695/a95aed9e-f524-06bb-8199-000ccc9eb3b9.png">


```swift
UIPasteboard.generalPasteboard().string = "hoge"
XCUIApplication().textFields["loginTextField"].pressForDuration(1.0)
XCUIApplication().menuItems["Paste"].tap()
```

#サンプルコード
前回の記事で、アラートが表示されるケースと画面遷移するケースを書き直してみました。

```swift
import XCTest

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

        // 画面の向きなどを指定したい場合はここで設定しておく
    }
    
    override func tearDown() {
        super.tearDown()
        
        XCUIApplication().terminate()
    }
    
    func testLoginFailedCaseMistype() {
        let app = XCUIApplication()
        let loginTextField = app.textFields["loginTextField"]
        
        loginTextField.tap()
        
        // ペーストボードに文字列"typo"をセット
        UIPasteboard.generalPasteboard().string = "typo"

        // テキストフィールドをロングタップ
        loginTextField.pressForDuration(1.0)
        app.menuItems["Paste"].tap()
        app.buttons["Done"].tap()

        app.buttons.element.tap()
        app.alerts.element.collectionViews.buttons["OK"].tap()
    }

    func testLoginFailedCaseNotInput() {
        
        let app = XCUIApplication()
        
        // ボタンタップをすぐにできなかった -> sleepしてからタップ
        sleep(2) //dispatch_afterでもできた

        app.buttons.element.tap()
        app.alerts.element.collectionViews.buttons["OK"].tap();
    }

    func testLoginSuccessful() {
        let app = XCUIApplication()
        let loginTextField = app.textFields["loginTextField"]
        
        loginTextField.tap()
        UIPasteboard.generalPasteboard().string = "hoge"
        loginTextField.pressForDuration(1.0)
        app.menuItems["Paste"].tap()

        app.buttons["Done"].tap()
        app.buttons["Login"].tap()
        
        app.buttons["back"].tap()

        loginTextField.tap()
        app.typeText("Misson complete!")
        app.buttons["Done"].tap()
    }
}
```

#課題
sleepやdispatch_afterなどで遅延させないとうまくいかない箇所がありました。
どのような場合に遅延処理が必要なのかまだイマイチわかっていません。

#さいごに
UITestのテストコードを書いてみてわかったことを整理しました。

UITest使いこなせれば便利そうだけれど、期待通りに動かすのが難しいです。