Swift環境での自動テスト導入と運用 2015/10/29(金) hireLink vol.9 [勉強会ログ]


iOSの著書も出していらっしゃる、新居雅行先生より、
テストの自動化と運用方法等々、ご教授いただきました！

個人的にこの言葉が印象に残りました。(ちょっと脚色していますw)
### 自動テストの導入で品質は何倍も上がる。入れるか入れないかという議論はもう終わった。問題はどう導入して運用するかだ。

テストコードを1行もかいたことのない私にはちょっと衝撃でした。。いい意味で。

下記よりログです。

※ 内容については私のフィルターがかかっているため、間違い・ご指摘があればこの記事へコメントをお願い致します。

## (改めて) テストの目的

テストの目的は、 **品質の担保**

(´・ω・｀) <「テストやった？」
(｀・ω・´)+ <「やりました！」

・・それって本当にちゃんとやったのか？

- 「正しく」「動く」だけでは曖昧 
- テストの基準を持つ

## CI(継続的インテグレーション)とテスト
- 要は頻繁にテストするってこと
- 自動テストを変更のたびに行う

- テストされていないコードを **「レガシーコード」** と呼ぶ
	- コードをかいたらそれは「レガシーコード」
	- [Michael Feathers:「レガシーコード改善ガイド」](http://www.amazon.co.jp/%E3%83%AC%E3%82%AC%E3%82%B7%E3%83%BC%E3%82%B3%E3%83%BC%E3%83%89%E6%94%B9%E5%96%84%E3%82%AC%E3%82%A4%E3%83%89-Object-Oriented-SELECTION-%E3%83%9E%E3%82%A4%E3%82%B1%E3%83%AB%E3%83%BBC%E3%83%BB%E3%83%95%E3%82%A7%E3%82%B6%E3%83%BC%E3%82%BA/dp/4798116831)
	- 改善ガイドでは、品質を設計の良し悪しでなく、テストの有無だけに寄せるという極論を解説

## レガシーコードを避けるために、自動テスト(テストコード)を開発
必ず言われるのが、
(´・ω・｀) < テストコードが間違ってたらどうすんのさ?
ぶっちゃけそれはしょうがないので、確かめて進むしかないそうです。


- 単体テスト
	- 1メソッドをテスト
	- **「こうあるべき」を記述することで仕様と強く結び付く** 
- 統合テスト
	- 完成品に対するテスト

### テストを主体に開発を進める

- テストを主体的に開発を進める
- **テストファースト** : テストコードを先にかく!
- TDD(TestDrivenDevelopment) : テストコードから実装コードを作る!? それは行き過ぎかもという意見も
- 関連：[テスト駆動開発（TDD）はもう終わっているのか？ Part 1 ](http://postd.cc/is-tdd-dead-part1/)

### 自動テストの重要性

仮に、AチームとBチームが共通して使っているメソッドXがあるとする。

- Aチーム: (｀・ω・´)< メソッドXのエラー判定は「null」を返そう!
- Bチーム: (´・ω・｀)< メソッドXのエラー判定は「誤入力」としてコードを返そう

・・もちろん、コンフリクトするためどちらかで問題発生！！
一見、コミュニケーションの問題や仕様書が作られていない問題にみえるが、

**もし、メソッドXの単体テストがあれば・・** 

- 「期待」をテストコードとして書けばいい
- **テストケース == 仕様** として共有可能
- 毎回チェックするので、問題に気付くタイミングも早い
- テストコードがなければ、最悪気づかずにリリースする可能性も・・

## Xcodeにテストプロジェクトを組み込む

[デモのソースコード【Github】](https://github.com/msyk/iOS_Samples/tree/master/iOS9_Primer/PlaceMap_Test)
Xcodeには単体テスト & UIテスト が内蔵されているので、新規プロジェクトを作成した時から入っている。
※ UIテストはXcode7から

![スクリーンショット_2015-10-31_18_36_08.png](https://qiita-image-store.s3.amazonaws.com/0/53303/dd5ffe13-464a-8d9e-9a6f-787ac1153243.png "スクリーンショット_2015-10-31_18_36_08.png")


## Xcode上での単体テスト

デモソース引用

```PlaceMapTests.swift
import XCTest
@testable import PlaceMap
class PlaceMapTests: XCTestCase {
    
    override func setUp() {
        super.setUp()
        // Put setup code here. This method is called before the invocation of each test method in the class.
    }
    
    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() {
        // This is an example of a functional test case.
        // Use XCTAssert and related functions to verify your tests produce the correct results.
        let pdb = PlaceDatabase()
        XCTAssertEqual(pdb.places.count, 47)
        XCTAssertEqual(pdb.prefectureOf("沖縄県"), "那覇市")      
    }
    
    func testPerformanceExample() {
        // This is an example of a performance test case.
        self.measureBlock {
            let pdb = PlaceDatabase()
            for var i = 0 ; i < 1000 ; i++ {
                pdb.prefectureOf("和歌山県")
            }
        }
    }
    
}
```

- @testable import PlaceMap(アプリケーションのターゲット名)というおまじないが必要
- 関数の頭に"test"とつけると、自動的にテストコードだと認識される

### XCTAssert

Assertは、主張する ・強調する という意味。

`XCTAssert(pdb.places.count, 47)`
(｀・ω・´) < pdb.placesの数は間違いなく 47 だから!! (こんなイメージ?)

これが間違っているかどうかがテスト結果になります

### テストコードを実行してみる

- 通常の実行で同時に動く
- 緑色のチェックマークがOK. 赤が返ってきたらエラー
- メソッド名の頭に Test がついてれば勝手にテストプラグラムとして実行してくれる

## Xcode上でのUIテスト
ドキュメントがあまりないので模索状態ではあるようです。
### レコーダーで自動コード生成
シミュレーターでの操作からコード生成してくれる。UIRecordingっていうらしい。
シミュレーターから入力するたびにライブでコードが生成されていくのでとても楽しい！が、日本語の情報を取得すると文字化けしてしまうようなのでここは手で直すしかない。
![スクリーンショット_2015-10-31_16_03_41.png](https://qiita-image-store.s3.amazonaws.com/0/53303/b7ccb478-98a4-381e-2c5a-1ead958c656e.png "スクリーンショット_2015-10-31_16_03_41.png")

### UITestの主な機能
プレゼン資料から引用です

- アプリケーションの呼び出し(起動)
	- `let app = XCUIApplication()`
- 要素群の取り出し（通常は子孫から探す）
	- `app.tables; app.tables.cells; app.staticTexts; app.buttons; app.navigationBars;` etc.
- 要素の特定
	- `app.cells.staticText["cell text"]`
	- `app.tables.elementBoundByIndex(0)`
- タップする
 	- `app.buttons.staticText["Done"].tap()`
- 検証する
	- `XCTAssertTrue(app.cells.staticText["cell text"].exists)` あるかないか
	- `XCTAssertEqual(app.tables.elementBoundByIndex(0).cells.count,47)`

サンプルコードより、
「mapを表示しているか」を「マップオブジェクトのカウントが1か」で判定しています。
`XCTAssertEqual(app.tables.count, 0)`

一応、座標で表示位置もチェック可能です。

```
let c = map.coordinateWithNormalizedOffset(CGVector(dx: 0.0, dy: 0.0)).screenPoint
XCTAssertEqual(c, CGPoint(x: 20.0, y: 72.0))
```

### UIテストは難しい？
フロントはどうしても"見た目"が入ってくるので、すべてを正確にチェックするのは難しいですが、
existsやequalを使えば、大量データのすべての表示をある程度チェックしたりすることは可能かなという所感。
スクショを撮る方法が見つからなかったんですが、もしできたらiphone4~6sの画面まで自動テストまわすだけで画面確認できそうなんだけども、やり方わかったら追記します(´・ω・｀)

## テストをかくにあたって 
- コードをかく量は確かに2倍になる - でも2倍時間がかかるかというとかからない
- テストファースト : 無理ならば **同時にセットで記述** してかくぐらいが落としどころ

### 単体テストがかきにくい場合
- まず、単一責任の原則(1メソッド1機能!)を守れているか？を見直す
- インターフェースと処理を分けてかけばできることもある
- つまり **コード設計もキレイになる** という副産物も

### 未完成の関連機能マター(特に通信周りや別のハード等)で作れない場合 
- [スタブ](http://e-words.jp/w/%E3%82%B9%E3%82%BF%E3%83%96.html)(代用品)を活用 → ダミーデータ入れちゃったりして分離

### すべてのケースをかく必要はない(ムダになる)
- 境界ポイント(-1, 0, +1)を重点的に
- エラーのケースを重点的に。無理やりつくる
- アプリケーション内のオブジェクト参照
	- 存在の可否 / 計数が重要
- 自動的にテストケース生成する例もある

## テストをスムーズに導入するには？

### チームに「テストをかく」文化が必要
- 本当に役に立つコードがかけるかというと、なかなか難しい
- マネジメント層の理解が不可欠
- 続けられる方法の模索。これは環境や状況次第なので答えはない
- **できるところからやればいい。** やってないところで問題が発生して有効性がわかる

### CIツールを活用
- [TraviceCI](https://travis-ci.org/)
- [OS X Server Xcode Server](https://www.apple.com/support/osxserver/xcodeserver/)

## +まとめ+
- レガシーコードはすでに論外
- 機械が自動的にテストすることで、確実に、頻繁に、テストを実施できる
- CIサービスやツールでテストをより効率的に！

### 個人的な所感として
テストコードはかいたことありませんが、これらのメリットを享受できるならぜひやるべきと思いました。

- 品質を高める
- コード設計のいい制約になる
- 仕様としての役割を果たす

１つ目は言うまでもないですが、やはり品質の担保というのは人間が行うのは限界があるものです。
特にスタートアップや小さいプロダクトの場合、どうしてもリリーススピードが優先になり、
テストが重要だとわかっていても疎かにしがちです。
テストこそ、本当はコンピュータに任せるべき工程かもしれませんね。（楽しい作業ではないですしw）

今回特に面白かったのは、うしろ２つの副産物です。
文化が重要という言葉がありましたが、
こういったメリットをみんなに周知するためには、まず自分が実感しないとですね。

みなさんも、素敵なテストコードで素敵なテストライフを！(｀・ω・´)+