UITableViewのUnitTestを書く

iOSアプリで、UITableViewが期待通りの挙動をするかのテストを書くメモです。

最近はStoryboardを使わずにレイアウトもすべてコードで書いているので、そのように書きます。

# 実装

持っているアイテムの数だけCellを表示する簡単な画面を想定します。

<img width="320" alt="スクリーンショット 2018-03-27 12.54.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/10057/226baf20-0f32-1500-2b43-bc7f80e2e724.png">


```swift
// ViewController.swift

import UIKit

class ViewController: UIViewController {
    
    let tableView = UITableView()
    var items = ["Hello", "World", "Swift 4", "Xcode 9.2", "iPhone X", "WWDC 2018"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(tableView)
        
        // TableViewを画面いっぱいに貼る
        tableView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: view.topAnchor),
            tableView.leftAnchor.constraint(equalTo: view.leftAnchor),
            tableView.rightAnchor.constraint(equalTo: view.rightAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
            ])
        
        tableView.dataSource = self
    }
}

extension ViewController: UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int {
        return 2    // ヘッダー + コンテンツ
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if section == 0 {
            return 1
        } else {
            return items.count
        }
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell()
        if indexPath.section == 0 {
            cell.textLabel?.text = "TableViewのテストのサンプルです"
            cell.textLabel?.font = UIFont.systemFont(ofSize: 14.0)
            cell.textLabel?.textColor = .gray
            cell.textLabel?.textAlignment = .center
        } else {
            cell.textLabel?.text = items[indexPath.row]
        }
        return cell
    }
}
```

この ViewController について、TableView のテストを書いていきます。


# テストコード

## 準備

UnitTest Bundleに `TableViewTestSampleTests.swift` というファイルを追加して、ここにテストを追加していきます。

まず、`@testable import TableViewTestSample` を宣言します。
これにより UnitTest から実装のコードを参照できるようになり、上記で準備して ViewController にアクセスできるようになります。

```swift
// TableViewTestSampleTests.swift

import XCTest
@testable import TableViewTestSample

class TableViewTestSampleTests: XCTestCase { ... }
```

## TableViewが表示されていることを確認するテスト

インスタンスが生成されていること、addSubviewされていることを確認します。

```swift
class TableViewTestSampleTests: XCTestCase {

    func test_tableViewが表示されること() {
        let vc = ViewController()
        XCTAssertNotNil(vc.tableView)
        XCTAssertTrue(vc.view.subviews.contains(vc.tableView))
    }
}
```

## Sectionの数を確認するテスト

Sectionは「ヘッダー」「コンテンツ」の2つあります。
確認するテストは以下のようになります。

```swift
import XCTest
@testable import TableViewTestSample

class TableViewTestSampleTests: XCTestCase {
    // ...

    func test_sectionが2つあること() {
        let vc = ViewController()
        
        let sectionCount = vc.numberOfSections(in: vc.tableView)
        XCTAssertEqual(sectionCount, 2)
    }
}
```

テスト名は `test` から始まっていれば自由に名付けられます。
最近は意味が分かりやすいよう、テスト名を日本語にしています。

# Rowの数を確認するテスト

1つ目のSectionにはヘッダーの1つだけ、
2つ目のSectionにはアイテムの数だけCellが表示されることを確認します。

```swift
class TableViewTestSampleTests: XCTestCase {
    // ...
    
    func test_1つ目のsectionにはセルが1つだけであること() {
        let vc = ViewController()
        
        let rowCount = vc.tableView(vc.tableView, numberOfRowsInSection: 0)
        XCTAssertEqual(rowCount, 1)
    }

    func test_2つ目のsectionには与えられたアイテムの数だけセルが表示されること() {
        let vc = ViewController()
        
        // ダミーのデータを設定する
        let testItems = ["test-item-1", "test-item-2"]
        vc.items = testItems
        
        let rowCount = vc.tableView(vc.tableView, numberOfRowsInSection: 1)
        XCTAssertEqual(rowCount, testItems.count)
    }
}
```

# Cellに表示されるラベルの内容を確認するテスト

期待通り Cell にテキストが表示されていることを確認します。

```swift
class TableViewTestSampleTests: XCTestCase {
    // ...

    func test_Cellのテキストに期待されたラベルが表示されていること() {
        let vc = ViewController()
        
        let testItems = ["test-item-1"]
        vc.items = testItems
        
        let cell = vc.tableView(vc.tableView, cellForRowAt: IndexPath(row: 0, section: 1))
        XCTAssertEqual(cell.textLabel?.text, "test-item-1")
    }
}
```

セルの表示に関するテストは ViewController ではなくセルのテストとして行うべきかもしれませんが、ここでは簡単のためこのクラスに書いてます。

また、Cell を取得する部分で UITableView のメソッドを使って、

```swift
vc.tableView.cellForRow(at: IndexPath(row: 0, section: 1))
```

このように記述することもできますが、これは表示中のセルしか取得できないので注意が必要です。

# まとめ

UITableView をテストするユニットテストの書き方をいくつか紹介しました。
タップイベントなども同じように書くことができるので参考にしてみてください。

サンプルコードはこちらに貼っておきます。

* [himaratsu/TableViewTestSample - GitHub](https://github.com/himaratsu/TableViewTestSample)

# 関連

* [UIButtonのUnitTestを書く](https://qiita.com/himara2/items/0c5a92400a3d617a6309)