技術書典6にて、タイトルでピンと来て、目次を見て「実際の業務に役立ちそう」と思ったので買いました。
私が担当しているプロジェクトではXCTest(Xcode標準の単体テスト)もXCUITest(Xcode標準のUIテスト)も導入できているのですが、もっとiOSならではのテスト手法を学びたいと思ったため、こちらの本を読もうと思いました。
通販は行っていないようなので、DLカード用のリンクのみ貼ります。
著者のkariadさんとは技術書典6の4日前に行われたOtemachi.swift #03という勉強会でお互いにLTしており、購入時に声を掛けさせていただいたら「ウホーイさんですか?」と覚えてくださっていたので嬉しかったです。
感想
iOSアプリのテスト手法について幅広くまとめられている本です。主な対象読者はテストを導入していない人ですが、ある程度テストを書いている人でもためになる本だと感じました。
大きく6章に分かれています。
- 1章:イントロダクション
- 2章:単体テストの実行方法
- 3章:単体テストしやすい設計手法
- 4章:サンプルアプリに単体テストを書く
- 5章:Bitriseを使って自動でテストを回す
- 6章:テスト戦略について
まず1・2章でiOSアプリの単体テストを実装して実行するまでの方法を説明されています。Xcodeのスクリーンショットなど絵を使っているので、単体テストを導入したことがない方でもわかりやすいと感じました。
個人的にですが、「2.3 テストケースの構成」 で紹介している「準備・実行・確認」の3フェーズは重要な概念なので、もっと詳しく説明してもいいと思いました。私はQuickが公開している「3つのA」がわかりやすくて感銘を受けたことがあります。
テストの実行方法について、私は行番号をクリックしたり、Xcode左のテスト一覧から再生ボタンを押したりして実行していたので、ショートカットキーの紹介はありがたかったです。
3章では主にDIを使ったテストしやすい設計を紹介されています。iOSアプリ以外でも通用する内容が多く、勉強になりました。
様々なデメリットがあるということで著者さんはsetter injectionを使っていませんでした。私はVIPERアーキテクチャでがっつり使っているので、できる限りconstructor injectionにリファクタリングしたいです。
Quickのfitやxitは使いどころがピンと来ていなかったのですが、ショートカットキーでテストを実行する場合は効果的だと思いました。
「テストは仕様を把握するためにも使える」この考え方はとてもいいと思いました。仕様書の作成とメンテナンスのコストを落とせますし、テストの見通しもよくなります。
テストの成功や失敗時に効果音を付けられるのは知らなかったです。こういった遊び心は大好きなので取り入れたいです。著者さんは好きなVTuberのセリフを成功時の効果音に設定しているとのことですが、どんなセリフなのか気になりますw
4章はサンプルアプリに単体テストを追加する方法を紹介されています。MVPアーキテクチャを知らないと多少難しい内容ですが、モックを使った具体的な実装方法を紹介されているので、勉強になります。
私はモックで関数が呼ばれたかどうかの変数をBool型で定義していました。そのため、int型で定義して呼ばれた回数をカウントすることで、複数回呼ばれていないことまで確認できる、という手法はぜひ取り入れたいと思いました。
5章はBitriseを使ってテストを自動で回す方法を紹介されています。まだCI/CDサービスを利用していない方は読むべき内容です。
Bitriseで証明書のインストールは2種類あり、「iOS Auto Provision」ステップを使う方がおすすめと書かれているのに、著者さんはもう1つの「Certificate and profile installer」ステップを使われていたので、その理由が気になりました。
6章は主にテスト戦略について紹介されています。他の章に比べ、著者さんの意見がたくさん詰まっているのが嬉しく、心に刺さる内容が多かったです。
単体テストとUIテストのメリットとデメリットを把握し、それらのテストの特徴と、テストピラミッドを考えてテスト戦略を立てる、という意見は素晴らしいと思いました。
今まで私はテストに対して「戦略」というものを考えたことがなかったので、心に刺さりました。
著者さんはUIテストの導入に懐疑的とのことです。私はプロジェクトによりますが、コストが高くてもUIテストを実行したい派です。理由として、リリース前に毎回手動でリグレッションテスト(システムテスト)を行うのが大変だからです。
私が担当しているアプリはiOS 8〜12までサポートしていて、月1〜2回の頻度でリリースしているので、2〜4週間のペースで、最低でもiOS 8.x, 9.x, 10.x, 11.x, 12.xの5OSでユーザーの操作をなぞるシステムテストを実行しています。このテストを省略するのはリスクが高いと思っています。全ケースを自動化することはまだ実現できていないのですが、一部でも自動化することでテストの負担がだいぶ減ることを実感できています。
学んだこと
Xcode
- テストを実行するショートカット
- 全テストの実行:⌘U
- 開いているファイルのテストのみ実行:⇧⌘U - スキーマの編集で[Randomize execution order]チェックをONにすることで、テストの実行順序をランダムにできる
- 環境設定からテストの失敗や成功時に鳴る効果音を設定できる
DI(Dependency Injection)
- DIパターンを適用することでテストしやすくなる
- DIの種類
- setter injection
以下のようにデメリットが多く、基本的には使うべきではない
- DIを強制しない
- 隠蔽したいプロパティでも公開する必要がある
- プロパティをvarにする必要がある
- constructor injection
setter injectionのデメリットを全て解消しているため、できる限りこちらを使うべき
- method injection
constructor injectionが使えない場合のみ使うのがいい - Date型の現在日時を返すメソッドやUserDefaultsなどは抽象化してDIするとテストしやすい
- バリデーションはブラックリストよりホワイトリスト形式の方が堅牢
- XCTContextを使うことでQuickのようにテストケースを構造化できる
テスト設計手法
- 関数が呼ばれたかどうかはBool型でなくint型で呼ばれた回数をカウントすることで、複数回呼ばれていないことまで確認できる
UIKit
- UIButton().sendActions(for:)メソッドを実行することでボタンタップイベントを発火できる
UIViewの場合はUIControlを使う
Nimble
- toEventually
遅延評価する
非同期のテストが成功しやすくなる - throwError
エラーの型をテストする
自動テスト
- 自動単体テストのメリット
- リグレッションテストがいつでも簡単に素早く実行できる
→安全にリファクタリングできる
- テストコードがそのまま仕様書になる
→他の人が仕様を把握しやすい - 自動単体テストのデメリット
- 設計によっては書くのが難しい - 自動UIテストのメリット
- ユーザーの操作をなぞることができる
→要は総合テスト(システムテスト)を自動化できる - 自動UIテストのデメリット
- メンテナンスコストが高い
→壊れやすいため
- 実行に時間がかかる
→実際にUIを操作するため
カバレッジ
- ステートメントカバレッジ(C0)
全命令を一度通ればOK - ブランチカバレッジ(デシジョンカバレッジ)(C1)
全分岐を一度通ればOK - Xcodeはステートメントカバレッジしか計測できない
→そのため、網羅率100%でも検知できない不具合がある
おわりに
テストが好きで、テストを書いていない人にもどんどんテストを書いてほしいという著者さんの思いが強く伝わってきました!
裏表紙の風景がどこの場所で、何を意味しているのか気になりました。