読者です 読者をやめる 読者になる 読者になる

try! Swift Artsyにおけるテスト手法の紹介 #tryswiftconf Day3-11

try! Swift 2016

Ash Furrow

iOSエンジニアおよび著者としてArtsyで働くカナダ人です。多くのアプリを開発し、これまで4冊の書籍を出版しています。また、オープンソースのコントリビューターとしてコミュニティに関わっています。ブログでは、興味深いプログラミングのネタからフィルムカメラにまでおよぶ様々なトピックを扱っています。

twitter.com

Artyは4つのiOSアプリケーションを提供しています。すべてオープンソースとして公開しています。そしてすべて異なるアプローチを用いてテストされています。なぜでしょうか?それぞれ異なるテスト手法を用いることは、異なる環境においてうまく動くからです。ArtsyのiOSチームの判断の裏付けとなった議論や、どんな問題にあたり、どのように課題を解決したのかを紹介します。すばらしいソフトウェアを作るために、なぜテストが重要なのかをより理解できるようになるでしょう。

f:id:niwatako:20160304174043j:plain

音楽と同じぐらい芸術を普及させたいと思っています。

4つのアプリを違うアプローチで単体テストしています

f:id:niwatako:20160304174108j:plain

2つはObjective-CですがSwiftにも適用できます。

3つの概念を頭においてもらいたいと思います。100%のカバー率を目指さず、テストをするだけでも価値がある。

原稿コンポーネントを出来るだけ分割すべき

新しいアプリもコンポーネントをできるだけ小さくする、バカバカしいぐらいがいいと思います。

f:id:niwatako:20160304174217j:plain

これが最初のもの。AppleTV用。いろんな都市の展覧会情報を見られる

f:id:niwatako:20160304174236j:plain

テストの戦略は、ありませんでした

f:id:niwatako:20160304174309j:plain

テストをすべきでない時がある、ということ、そして、皆さんにもしていない人、居ますよね?iOSのコミュニティでは新しい概念かもしれません、テストというのは。

f:id:niwatako:20160304174343j:plain

時間が短かった、一人が隔離されて開発していた、ずっと。メンテナンスも最小限で新機能追加もなかった。目的が一つしかないシンプルなものでした。

テストをすべきかどうか

f:id:niwatako:20160304174423j:plain

バランスが重要です。

同僚に作りなおしてと言っても2週間程度でしょう。ただその時はもっと短かったのです。

テストすべきか?そうです。でも必ずしもやらねばならないわけではないです。

f:id:niwatako:20160304174451j:plain

2つめ

f:id:niwatako:20160304174513j:plain

テストを後から追加した、画廊のアプリです。コードベースはさっきのよりもずっと大きく、壊れる可能性が高かったです。またAPI同期化する部分にバグを仕込んでは困るということで、テストをすることになりました。

何よりも重要なのはバスファクタ、個人に重要なバスが集中している、この人がバスに轢かれたら大変ということです。

f:id:niwatako:20160304174622j:plain

個人が持っている知識を共有する必要があった。アプリがどのように動作するのか?

f:id:niwatako:20160304174644j:plain

良いテストはアプリの外側の動きを表現します。

実はこのテストが良かったのはDI(依存性の注入)を使ったのが良かったです。

f:id:niwatako:20160304174734j:plain

f:id:niwatako:20160304174756j:plain

シングルトンではなく管理されたものでインジェクトします。インメモリコアデータをコンテキストのインメモリにおいておきます。テストをするオブジェクトに入れてオブジェクトがなにか処理をして、検査して予想通りに動いたか確認します。

概念的にはこのような形です。

f:id:niwatako:20160304174854j:plain

RSpecスタイルのテストを使いました。共通処理を見出して切り出します。

f:id:niwatako:20160304175003j:plain

更にリファクタリング

f:id:niwatako:20160304175029j:plain

前処理でグループ化して入れ子になる。最初のテストスイートを見ると3つのテストが有ります。可読性がないです。

f:id:niwatako:20160304175055j:plain

見やすいですね

f:id:niwatako:20160304175112j:plain

どれだけコンテキストが有り、ネストされ複雑で、長さなのか、見て分かる。ネストがあまりにも深い場合はオブジェクトが複雑すぎる。

f:id:niwatako:20160304175200j:plain

テストを追加すればドキュメントになります。テストは内部ではなく挙動をテストします。理想的には各クラスパブリックファンクションは一つにするべきです。DIはモックを活用するとよいです。RSpec系は完結で記述力の高いテストが出来てよいです。


アートのアプリ

f:id:niwatako:20160304175303j:plain

急いで作って後でテストを加えた。コードがたくさんあって大勢の開発者が関わって買い方が違ってテストが大変。

f:id:niwatako:20160304175350j:plain

そしてネットワーキング処理がアプリ中に散在していました。

f:id:niwatako:20160304175417j:plain

3人でやっているのでやり方の違いがある。

またiPhoneだったアプリがユニバーサルに変わった。

f:id:niwatako:20160304175447j:plain

f:id:niwatako:20160304175453j:plain

SharedExampleをやった。テストを合格させるときにはiPhoneiPadで見ていく。

もともとテストを追加するときには最大のクラスからテストしました。これが最も重かったのでプライオリティが高かったのでここからだと思った。でもこれをテストするには内部をテストしなくてはいけないことになり、内部実装を変えるとテストを沢山書き換えなくてはいけなくなった。

コードに変更を加えたら必ずテストをするようにしています。

f:id:niwatako:20160304175626j:plain

私が好きなのはスナップショットのテストです。

ビューの写真を保存してPNGレポジトリに保存していき、全く同じ設定で同じデータをレビューし、スナップショットを比較します。何か違ったらテストは不合格です。すごく便利です。ただリポジトリがすごく大きくなります。そう入ってお間違ってUIに変更を加えた時に検知しやすいです。PRを使ってレビューしている時も便利です、どう変化したかがわかりやすいです。PR見るのがわかりやすくなります。

どうしたらいいのか分からなくなったら他のエンジニアに生きます。

f:id:niwatako:20160304175802j:plain

既存クラスを小さな要素にしてテストをしていくと楽にできます。始めたばかりのひとは小さなクラスからテストしてください。チームでルールを作り新しいコードはテストするようにし、最近の変更はテストしましょう。どうしていいかわからない時は効きましょう、iOSコミュニティは皆が助けてくれます。場合によってはよくないコードを描くこともある。そういう時はドキュメントを残し、どう治したいか残しましょう。


アートオークションアプリ

f:id:niwatako:20160304175953j:plain

Enterpriseで、苦労をしたりしました。今でも技術的負債が残っています。

これが私たちの最初のSwiftアプリです。すべてが変わっていっていて、テストをすることで壊れないか確認することが出来まいた。 単体テストが唯一、最初のSwiftアプリケーションではわたしたちに慣れ親しんだものでした、Swiftの仕組みを理解することにも繋がりました。

QuickはRSpecスタイルのテストライブラリです。開発中にQuickを使い、開発中に質問を送ったりしました。Quickによって素晴らしいRspecスタイルのテストライブラリを手に入れました。ニンブルも。

f:id:niwatako:20160304180201j:plain

良いテストは短いはずです。調整してアクションを取ってアサーションをとります。

アレンジメントはほとんどのメソッドの最初でやります。アサーションはテストするクラスの振る舞いが期待通りかチェックします。これをExoectationと呼びます。

ニンブルマッチャ−とは何でしょう?

f:id:niwatako:20160304180307j:plain

シンプル!

f:id:niwatako:20160304180317j:plain

ここまで出来る!!

ニンブルは等式用マッチャーだけではありません。ビルドインマッチャ−をストリング用にもArrayようにもRangeようにも有ります。


f:id:niwatako:20160304180408j:plain

効果的テストは? * RspecStyle * 短いテストを描く * たくさんある

まとめ

  • 完璧であろうとは思わない。理想的でないコードでも良い。ヘルプを求めましょう
  • コンポーネントを小さくしましょう
  • クラスを小さく、パブリックインターフェースは1つにしましょう。
    • クラスの制約を書けることでテストしやすくなります。小さくすることで苦労を最低限に出来ます。

お話したアプリはGithubでご覧いただけます。

f:id:niwatako:20160304180615j:plain

QA

テスト駆動好きだが、「重要だが、コストがかかる、サーバーサイドが完璧ならクライアントはいいのではないですか?」

Swiftは方が強力なのでテストは要らないというひとも居ます。コンパイルできたら実行できますから。でもコンパイルしたのに動かないということはよく有ります。アプリケーションの振る舞いを文章化出来る、クリティカルな部分が壊れないようにするとか、サーバーサイドと同じように重要だと私は思います。バグを入れたまま出荷するとAppStoreのレビューもありますし。

スナップショットテストとおっしゃっていましたが、UIテスト、結合テストはどのように位置づけていますか?

決めかねているとことがありますね。Xcodeのものもあり決めかねていますが、スナップショットが結構十分だと思っています。

初歩的な質問ですが、まだテストを書いていません。知り合いにTDDの初歩的なコーチングを受けましたが、テストを各時に試行の順序として、どのような機能をテストしたいか考えて、テストから先に書くという話をされた。普段の感覚で考えると実装から考えてしまい、単体の機能で考えるのが難しい。そこに良いアドバイスはあるか

TDD テスト駆動開発ですね 結構対立を生んだり色んな意見があり好きな人も嫌いな人も居る。私は中間に居ます。私はコードを書くのが好きですごく気が短いので実装を書くケースも有ります。分かっているけどやってしまいます。ただコードを書いて、クラスを書いて、テストが難しいというケースも出てくるのでそういう場合はクラスを変更します。それを繰り返すとテストが楽なクラスを書けるようになって来ます。そういった経験をしたから越しできることだと思うので、TDDと同じぐらいの価値があると思います。同じような堅牢性は無いかもしれませんが、十分だと思っています。

アドバイスは、クラスは小さめに。テストに苦労したら幾つかに分けて個々にテストし、DIを使って連携してテストできるか試してみてください。

https://twitter.com/takecian/status/705680263154528256

気に入った記事は はてなブックマーク

はてなブックマークアプリiOS開発チームから来ました! はてなブックマークにはSwift特集があります! 良い記事を見逃さないように、ご利用ください! http://b.hatena.ne.jp/hotentry/it/Swift

そして良いまとめ記事があったらはてなブックマークでブックマークしましょう! try! Swift の記事で盛り上がると嬉しいです!