テストを行う。

# 対象読者
・swiftを始めたばかりな方
・Testをまともに書いたことのない方
・Testって何？な方
・swiftすきな方

# Testの大切さ
まず、動作確認(Test)はとても大切です。
作ったアプリがきちんと動くか、実行して自分で触って予定通りの動作を確認する。

これはとても大切でリリースした後にバグ祭りなんて自体を少なからず避けられます。

## Testは何のためにするのか？
Testをするのは
・動作の確認の為
・動作を保証する為
・変更に耐えられる様にする為

これらが挙げられます。
その為、これが保証されれば、「自分で実行させて触って動作を確認する」というTestでもいいのです。

## 手動Testは現実的か？
実際、開発が進むたびに手動でテストするのはとても気が遠くなります。

アップデートを施せば、手動テストで通っていた場所も当然の様にバグが顔を出します。(ｺﾝﾆﾁﾊ!!!)

## Testを書くのはめんどくさい。
Test書いたことない頃は「Test書くのめんどくせえ」と筆者は思っていました。

**というか正直Testを書いている今でもめんどくさいです。**


ただ、開発スピードを考えても自動でテストを行うほうが**速い**し、長期的には**かなり楽**になります。

# 簡単なTestを書いてみよう。

プロジェクトを作る際にXCTestとXCUITestを追加しておきます。
(後から追加できます。)

## 単体テスト

### 単体テスト(同期)

テスト対象は以下の様に、よくある[ただ足し算をする関数。](https://github.com/airy-swift/TestSample/blob/76af1c9b4561d98ccb63c3b377554be5c71470da/Qiita/Model/SyncModel.swift#L9)

```swift:SyncModel.swift
import Foundation

final class SyncModel {
    func add(_ x: Int, _ y: Int) -> Int {
        return x + y
    }
}
```
そして以下がその[テストコード。](https://github.com/airy-swift/TestSample/blob/76af1c9b4561d98ccb63c3b377554be5c71470da/QiitaTests/QiitaTests.swift#L38)

```swift:XCTest
func testSyncAdd() throws {
    let x = 10
    let y = 5
    let ans = syncModel.add(x, y)
    
    XCTAssertEqual(ans, 15)
}
```

XCTAssertEqualは二つの引数を比較して一致しなければエラーを投げます。

### 単体テスト(非同期)

次に[非同期のテスト。](https://github.com/airy-swift/TestSample/blob/76af1c9b4561d98ccb63c3b377554be5c71470da/Qiita/Model/AsyncModel.swift#L9)
closureに計算結果を渡すものです。

```swift:AsyncModel
import Foundation

final class AsyncModel {
    func add(_ x: Int, _ y: Int, completion: @escaping (Int) -> Void) {
        DispatchQueue.global().asyncAfter(deadline: .now() + 3) {
            completion(x + y)
        }
    }
}
```

そして以下が対象の[テストコード](https://github.com/airy-swift/TestSample/blob/76af1c9b4561d98ccb63c3b377554be5c71470da/QiitaTests/QiitaTests.swift#L27)

```swift:XCTest
func testAsyncAdd() throws {
    let exp = expectation(description: "add function")
    let x = 10
    let y = 5
    asyncModel.add(x, y) { ans in
        XCTAssertEqual(ans, 15)
        exp.fulfill()
    }
    
    wait(for: [exp], timeout: 5)
}
```

expectationを使って非同期処理のテストを行ってます。

wait関数を記述しなければ**非同期処理を待たずにこのテスト関数は終了**し、**正しいテストはできません**。

ではwaitは何を待っているのでしょうか？
それは引数forで指定したexpが**fulfill**を投げるのを待っています。

非同期処理が終わったタイミングでexpがfulfillを読んでいるのが確認できますね。
これでwait以下が実行されるので段階的に実行することができるというわけですね。

## UITest

例として遷移のテストを行います。
[コード](https://github.com/airy-swift/TestSample/blob/f6aa9d45bbd308e022398e33b0400df22e55bdd4/Qiita/ViewController/ViewController.swift#L11)はここに書くには少し鬱陶しいので簡単に説明します。

「ボタンを押したら別のViewControllerに遷移するアプリ」です！！！

これに対する[テスト](https://github.com/airy-swift/TestSample/blob/f6aa9d45bbd308e022398e33b0400df22e55bdd4/QiitaUITests/QiitaUITests.swift#L26)は

```swift:XCUITest
func testPush() throws {
    let app = XCUIApplication()
    app.launch()

    let pushButton = app.buttons["pushButton"]
    XCTAssert(pushButton.exists)
    
    pushButton.tap()
    let backButton = app.buttons["Back"]
    XCTAssert(backButton.waitForExistence(timeout: 5))
        
    backButton.tap()
    let pushButton2 = app.buttons["pushButton"]
    XCTAssert(pushButton2.waitForExistence(timeout: 5))
}
```

まず、app.launchでアプリを起動します。

次に遷移するためのボタンを取得します。
これは、UIの宣言側で

```swift:ViewController
button.accesibilityIdentifier = "pushButton"
```

などと設定することでTest側でUIを特定できる様にします。
Test側は```app.buttons["pushButton"]```で取得できるのですからよくできています。(ｳﾝｳﾝ)

プッシュされた先のViewControllerでは設定していなければ左上のナビゲーションアイテムとしてBackボタンが設定されているはずです。

そのため、

```swift:XCUITest
let backButton = app.buttons["Back"]
XCTAssert(backButton.waitForExistence(timeout: 5))
```
この様にBackボタンを取得、存在を確認することでテストを行います。

## 結合テスト

単体テストでやった単純な足し算関数とUIテストを組み合わせてテストします。

[テスト対象](https://github.com/airy-swift/TestSample/blob/f6aa9d45bbd308e022398e33b0400df22e55bdd4/Qiita/ViewController/ViewController.swift#L11)は「足し算の入力を設定する2つのテキストフィールドとボタンを持ち、遷移先には足し算の結果を出力するラベルを持つアプリ」です！！！

これに対する[テストコード](    func testAdd() throws {)は以下の様になります。

```swift:XCUITest
func testAdd() throws {
    let app = XCUIApplication()
    app.launch()
        
    let xField = app.textFields["xField"]
    let yField = app.textFields["yField"]
    XCTAssert(xField.exists)
    XCTAssert(yField.exists)
        
    let pushButton = app.buttons["pushButton"]
    XCTAssert(pushButton.exists)
        
    xField.tap()
    xField.typeText("10")
        
    yField.tap()
    yField.typeText("5")
        
    pushButton.tap()
    let backButton = app.buttons["Back"]
    XCTAssert(backButton.waitForExistence(timeout: 5))
       
    let ansLabel = app.staticTexts["ansLabel"]
    XCTAssert(ansLabel.exists)
        
    XCTAssertEqual(Int(ansLabel.label), 15)
}
```

例によってテキストフィールドはaccesibilityIdentifierにxField, yFieldを指定し、取得しています。

取得したUIにtapでフォーカス、typeTextで文字を入力してもらう動作をしていますね。

```swift
xField.tap()
xField.typeText("10")
```

続いて一つ前のUITestでやっていた様に遷移します。
遷移はBackボタンが取得できているかで完了確認を簡易的に行っています。

```swift
let backButton = app.buttons["Back"]
XCTAssert(backButton.waitForExistence(timeout: 5))
```

textFieldsやbuttonsなどのUIはそのままの名前だったので迷わず取得できますが、
ラベルはstaticTextsから取得します。紛らわしいですね！

ラベルのテキストはlabelプロパティで取得します。

値がしっかり反映されているかを確認してテストを終えます。

```swift
XCTAssertEqual(Int(ansLabel.label), 15)
```

# 如何だったでしょうか？

テストはとても大事です。**(「「「２回目」」」)**

ただ、今までテストを書いたことがない人がいきなり完璧なテストを網羅的に書くのも、現実的ではありません。

まずは一つ、簡単なテストを書いてみては如何でしょうか？

### ご愛読ありがとうございました。(完)