XCTestの結果を、どう解釈するのか考えてみた

## 2020/10/2 変更
参考記事・図書の記載に一部誤りがあったことをご指摘いただき、修正いたしました。
ご指摘いただきました [y-okudera](https://qiita.com/y-okudera)様、ありがとうございます!
ならびに、関係者の方々にはお詫び申し上げます。


# はじめに

こんにちは、iOSエンジニアの dayossi です。

家族が幸せになれるサービスを提供したいと思って、
[HaloHalo](https://hallo-hallo.sakura.ne.jp/home/)という家族日記アプリをリリースしています。

今回は、ユニットテストの結果を
どう解釈したらいいのか悩んだので整理しました。

今回の成果物はコチラです。
https://github.com/Kohei312/CollectioView_loose-coupling

# そもそも、テストを書く目的って？

テストの目的は、
記述した処理が、きちんと意図したとおりに動けているのか検証することです。

テスト結果の評価には、コードカバレッジが活用できます。
XCTestではコードカバレッジのうち、ラインカバレッジの結果が簡単に可視化でき
行単位でどの処理が活用されている/いない のかが抽出できるようになります。

Xcodeのラインカバレッジでは、コメント文を含め
その行を通り抜けたかどうかをチェックします。

また、ビルドする対象を管理するスキームを変更すると
テスト環境下でのカバレッジ計測の結果を % で見ることができます。
（個人的に、ここの設定が抜けていてカバレッジ計測どうやるんだ!?っとハマってました...）
![スクリーンショット 2020-10-02 12.23.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730098/176dd3db-aead-3975-8887-fa5197733ef3.png)



# いざ、評価

今回は、HTTP通信に関する簡単なテストを組みました。
testAPI()からcallAPI()を呼び、結果をコールバックで受けるまでの流れを検証しました。
![スクリーンショット 2020-10-02 11.16.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730098/f80443d5-7d3c-aee9-03cc-e350fd3a43a0.png)

テストの結果、testAPI()自体は呼ばれ、callAPI()も実行されていますが
callAPI()のクロージャが呼ばれていない結果が得られました。

callAPI()の中で、きちんとcompletionが呼ばれていないことが原因でした。
（あり得ないエラーですが...）

![スクリーンショット 2020-10-02 11.17.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730098/288699a0-9a98-091e-84ec-c114e8087e3f.png)

callAPI()内でcompletionを呼んであげたら、無事に通過しました。


上記の様に、1文にメソッドの呼び出しとコールバック後の処理の2つが書かれていても
それぞれの処理について、通過したかどうかを検証することができます。


以下のように、カバレッジの結果を確認できます。
![スクリーンショット 2020-10-02 14.31.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730098/08212d63-e1b7-d1cf-8c3f-74b57a0f378a.png)

# 解釈
HTTP通信のリクエスト・レスポンスに関しては、テストの結果から
コード内部の記述について改善が見られたと考えられます。

ですが、実装しているクラスと比較するとカバレッジに大きな差があります。
![スクリーンショット 2020-10-02 14.36.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730098/23328a55-ded8-4198-efc3-bb0172587732.png)


実装クラスの中身を見てみると、エラー文の中を通過しており
guard文でのアンラップに失敗しています。
![スクリーンショット 2020-10-02 14.42.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730098/d3ba550b-e9a8-925d-2588-f57fe8717c8c.png)

つまり、なんらかの理由でURLSessionが失敗したため
テストの結果と差異が生まれたことがまず考えられそうです。
（実際、ネットワーク通信を切断した状況下で行っていたので整合性がありました。）


この解釈から、処理内部のコード記述ミスなのか
外部要因によるエラーなのか
問題点を少しずつ切り分けて考えることもできるんだな〜と実感できました。


# まとめ
XCTestで把握できるのは「コード内部の処理通過状況」であり、
コンポーネントごとの処理動向を把握することができるんだなと実感しました。

ただし、基本的にネットワーク通信の環境から切り離されているので
ネットワーク通信を伴うコンポーネントのテストを行いたいときは、
HTTPスタブライブラリを活用するほうが便利だなと思いました。

副産物として、「テストを書きにくいな〜」と思ったコンポーネントには
複数の責務が混じっていて、簡素化できる余地を発見できる点も
テストを書くメリットだなと思いました。

テストありきで設計をすすめる考えも学んでみたいと思います！
（温かいツッコミを、お待ちしております）

# 参考図書・記事

[松尾 和昭,細沼 祐介,田中 賢治 他.iOSテスト全書(2019).PEAKS出版.](https://peaks.cc/books/iOS_testing)

[Xcode Code Coverage (Apple 公式ドキュメント)](https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/testing_with_xcode/chapters/07-code_coverage.html)
[単体テストのテスト項目の観点](https://softwarenote.info/p738/)
[テストを制する者は開発を制す🏀](https://qiita.com/Kaito_Noguchi/items/37fa4c240f231d0b1d95)
[XCTest入門Part1 (Swift)](https://qiita.com/y-okudera/items/97f9d6026d0da59fef38)
[Swiftユニットテスト入門](https://speakerdeck.com/yoshiyuki_tanaka/swiftyunitutotesutoru-men?slide=41)
[Differences between Line and Branch coverage
](https://stackoverflow.com/questions/8229236/differences-between-line-and-branch-coverage)
[【読書】現場に導入するためのiOSアプリテスト](https://medium.com/@theuhoooi/ios-test-for-field-a38e76f31ce7)
[iOSDC Japan 2019: Xcodeのコードカバレッジ計測ではなぜブランチカバレッジが取れないのだろうか？ / kariad](https://www.youtube.com/watch?v=LZfw21hgRGk)

# 今後、チャレンジしてみたい...
[XcodeのコードカバレッジをHTMLなどで出力する方法](https://qiita.com/uhooi/items/e1e464777d2163286c59)
[iOSで振る舞いを見るテストを書いて安心しながら開発を続けよう](https://techblog.yahoo.co.jp/advent-calendar-2018/ios/)
[コードを書きながら同時に設計していくためのiOSアプリ開発方針](https://techblog.yahoo.co.jp/entry/20191202785270/)