【Swift】初めてのUITest導入

# はじめに

初心者が初めてXcodeでUITestingを導入した際の備忘録です。
個人で開発している分にはなかなか導入することも少ないかと思いますが、初めてテストを導入してみようという方などに参考になればと思います。

簡単なサンプルアプリケーションを[こちら](https://github.com/taji-taji/UitestingSample/tree/master)に用意しました。このサンプルアプリケーションも交えつつ書き進めていきます。

# 環境

Xcode7.2

# 1. プロジェクトにUITestsターゲットを追加

- 新しいプロジェクトにUITestを含める場合
- 既存のプロジェクトにUITestを加える場合

## 新しいプロジェクトにUITestを含める場合

新規プロジェクトを作成し、「`Choose options for your new project:`」の画面で**`Include UI Tests` にチェックを入れます**。

![スクリーンショット_2016-01-10_22_01_27.png](https://qiita-image-store.s3.amazonaws.com/0/11914/69c11a68-368c-d139-202e-8d03346a953c.png)

この状態でプロジェクトを作成するとUITestのターゲットができるので、準備完了です。

![スクリーンショット_2016-01-10_22_09_11.png](https://qiita-image-store.s3.amazonaws.com/0/11914/f86adc74-8c19-7e1c-f3be-d20d675f1251.png)

![スクリーンショット 2016-01-10 22.11.09.png](https://qiita-image-store.s3.amazonaws.com/0/11914/8a62eef8-bfcd-2ba9-05a7-0679c10ede42.png)


## 既存のプロジェクトにUITestを加える場合

「File」→「New」→「Target...」を選択
![スクリーンショット_2016-01-10_22_14_54.png](https://qiita-image-store.s3.amazonaws.com/0/11914/55ec1247-c445-b12a-26d0-1149745311b2.png)

「iOS」→「Test」→「iOS UI Testing Bundle」を選択して「Next」
![スクリーンショット_2016-01-10_22_15_12.png](https://qiita-image-store.s3.amazonaws.com/0/11914/1c2b5606-e3a3-22ff-b073-46b5d438e041.png)

「Finish」をクリックすると、UITestを含めた状態で新規プロジェクトを作成した時と同じようにUITestのターゲットができます。
![スクリーンショット_2016-01-10_22_15_23.png](https://qiita-image-store.s3.amazonaws.com/0/11914/70613625-4d49-bdd7-b6c5-315dc5ccd570.png)


# 2. アプリケーションコードを作成

新規でプロジェクトを作成した場合はアプリケーションを作成しましょう。
既存のプロジェクトでアプリケーションができている場合は特に問題ないです。

サンプルアプリケーションでは単に掛け算をするだけのアプリケーションを作成しました。

# 3. UITestを書く

先ほど作成したUITestターゲットの中に「`[ProductName].swift`」というファイルがあるかと思います。
※[ProductName]はターゲット作成時につけた名前。新規プロジェクトから自動的に作られた場合は`[ProjectName]UITests.swift`となっている。

```swift:[ProductName].swift

import XCTest

class UITestSample2UITests: XCTestCase {
        
    override func setUp() {
        super.setUp()
        
        // Put setup code here. This method is called before the invocation of each test method in the class.
        
        // In UI tests it is usually best to stop immediately when a failure occurs.
        continueAfterFailure = false
        // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
        XCUIApplication().launch()

        // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
    }
    
    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        super.tearDown()
    }
    
    func testExample() {
        // Use recording to get started writing UI tests.
        // Use XCTAssert and related functions to verify your tests produce the correct results.
    }
    
}
```

この中にテストコードを書いていきます。
テストのメソッドは初めから書いてある`testExample`のように先頭に`test`とつけます。
（`func testMyLabelExists()`みたいな)

ここからはサンプルアプリケーションを用いて説明していきます。

## サンプルアプリケーションについて

サンプルアプリケーションは、
左辺のテキストフィールドと右辺のテキストフィールドに数値を入力して「=」ボタンをタップすると、「=」ボタンの右側のラベルに掛け算の答えが表示されるという内容になっています。
また、仕様として以下を満たしています。

- アプリ起動時にテキストフィールド及びラベルに初期値は入っていない
- 左辺または右辺のテキストフィールドに数値が入力されていないと「=」ボタンが押せない

※サンプルアプリケーションは簡単に作成したものなので、内容・仕様などはかなり適当です。ご容赦ください :sweat_smile: 

## テストの作成

では、このアプリケーションで次のような手順でテストをしてみます。

1. アプリ起動時
    - 「=」ボタンがdisableかどうか
2. 左辺に「12」を入力
    - 「=」ボタンがdisableかどうか 
3. 右辺に「4」を入力
    - 「=」ボタンがenableかどうか
4. 「=」ボタンをタップ
    - 答えが表示されるラベルに「48.0」と表示されているかどうか

これらを実際に書いたのが以下のコードになります。（testExample部分）

```swift:UitestingSampleUITests.swift
import XCTest

class UitestingSampleUITests: XCTestCase {
        
    override func setUp() {
        super.setUp()
        
        // Put setup code here. This method is called before the invocation of each test method in the class.
        
        // In UI tests it is usually best to stop immediately when a failure occurs.
        continueAfterFailure = false
        // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
        XCUIApplication().launch()

        // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
    }
    
    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        super.tearDown()
    }
    
    func testExample() {
        
        let app = XCUIApplication()
        
        // 「=」ボタンを取得
        let resultButton = app.buttons["resultButton"]
        
        // 「=」ボタンがdisabled
        XCTAssertFalse(resultButton.enabled)

        // 左辺のテキストフィールドを取得
        let leftValueTextField = app.textFields["leftValueTextField"]
        // 左辺のテキストフィールドをタップしてフォーカスを当てる
        leftValueTextField.tap()
        // 12と入力
        leftValueTextField.typeText("12")
        
        // 「=」ボタンがdisabled
        XCTAssertFalse(resultButton.enabled)
        
        // 右辺のテキストフィールドを取得
        let rightValueTextField = app.textFields["rightValueTextField"]
        // 右辺のテキストフィールドをタップしてフォーカスを当てる
        rightValueTextField.tap()
        // 4と入力
        rightValueTextField.typeText("4")
        
        // 「=」ボタンがenabled
        XCTAssertTrue(resultButton.enabled)
        
        // 「=」ボタンをタップ
        app.buttons["resultButton"].tap()
        
        let resultLabel = app.staticTexts["resultLabel"]
        
        // 結果を表示するラベルのテキストが「48.0」になっていれば成功
        XCTAssertEqual(resultLabel.label, "48.0")
    }
    
}
```

### オブジェクトの取得方法

テストコードを書く時に、画面上のボタンやテキストフィールドを取得してそのオブジェクトをコードからタップしたりテキスト入力したりします。
まず、その際のオブジェクトの取得について簡単に説明します。

オブジェクトの取得方法はいくつかあるかと思いますが、今回はオブジェクトに`accessibility identifier`を設定し、そのidentifierを指定して取得しています。
例） `app.buttons["resultButton"]`の`resultButton`が設定したidentifier


#### `accessibility identifier`の設定方法
コードから設定する場合は

```swift
resultButton.accessibilityIdentifier = "resultButton"
```
というふうに書きます。

Interface Builderから設定する場合は
identity inspectorのAccessibilityから設定できます。
![スクリーンショット 2016-01-10 23.10.12.png](https://qiita-image-store.s3.amazonaws.com/0/11914/c3c2260f-b61b-a171-0f17-af61204cbd46.png)

サンプルアプリケーションでは全てIBから設定しています。

また、オブジェクトによって`XCUIElementQuery`(app.buttonsのbuttons部分)が違うので、いくつかになりますが簡単に紹介します。

```
- UIButton                 : buttons
- UITextField              : textFields
- UITextField(secure text) : secureTextFields
- UILable                  : staticTexts

```

つまり、サンプルアプリケーションの`app.buttons["resultButton"]`は、
`accessibility identifier`が`resultButton`である`UIButton`を指していることになります。

### Assertの挿入

次に、実際にテストで検証する部分を記入していきましょう。
サンプルでは、`XCTAssertTrue(resultButton.enabled)`や`XCTAssertEqual(resultLabel.label, "48.0")`といった部分ですね。

いくつか検証のパターンを紹介します。

- オブジェクトが存在するかどうか

```swift
XCTAssert(someElement.exists)
```

- labelのテキストが想定したものになっているか

```swift
XCTAssertEquals(myLabel.label, "想定の文字")
```

- ボタンが押せるか

```swift
XCTAssertTrue(myButton.enabled)
```

#  4. テストの実行

テストコードが書けたらテストを実行しましょう。

テストを実行するには、
1. XcodeのRunボタンをTestボタンに変更して実行（もしくは[cmd+U]で実行）するか、
2. テストクラス・テストメソッドの左側にある再生マークをクリックして実行させることができます。

1の場合
![スクリーンショット_2016-01-10_23_54_21.png](https://qiita-image-store.s3.amazonaws.com/0/11914/aab929f6-9839-0d99-33e5-b6a7dc987df4.png)

2の場合
![スクリーンショット_2016-01-10_23_57_45.png](https://qiita-image-store.s3.amazonaws.com/0/11914/9856e268-ba21-4484-c02a-e3ffface50f9.png)

1の場合は、UnitTestも含めて全てのテストが実行されますが、2の場合はクラス単位やメソッド単位で実行することができます。


[サンプルアプリでのテスト実行動画 (YouTube)](https://youtu.be/WkKmg2SGMfI)


# 5. テスト結果の確認

テストが通れば、再生マークのところが緑色に、通らなければ赤色になります。

![スクリーンショット 2016-01-11 0.02.31.png](https://qiita-image-store.s3.amazonaws.com/0/11914/25d58c12-3b10-d63d-621f-469ca460b55a.png)

![スクリーンショット 2016-01-11 0.03.06.png](https://qiita-image-store.s3.amazonaws.com/0/11914/09580588-c27a-9e6f-c2c1-3b8e327eb1fb.png)

通らなかった時はエラー箇所を教えてくれるので、修正して再度テストをしてみましょう。

Xcodeの`Report Navigator`からテスト詳細な結果を見ることもできます。

![スクリーンショット 2016-01-11 0.08.33.png](https://qiita-image-store.s3.amazonaws.com/0/11914/2f8789df-8bd4-d9af-ae35-9acab93d3490.png)


# さいごに

以上でざっくりになりますが、導入方法を書いていきました。
不備などあればご指摘いただければ幸いです。
また、より詳しい内容やもっと良い記事もあるので、下記に紹介させていただきます。


# 参考

https://developer.apple.com/videos/play/wwdc2015-406/
http://masilotti.com/xctest-documentation/
http://dev.classmethod.jp/smartphone/iphone/xcode-7-ui-testing/
https://github.com/joemasilotti/UI-Testing-Cheat-Sheet
http://masilotti.com/ui-testing-cheat-sheet/