こんにちは。ニフティライフスタイル株式会社でアプリ開発をしている松居です。
この記事はニフティグループ Advent Calendar 2018の14日目の記事です。
昨日は私のチームの先輩 @hicka04 さんの「【Swift】JSON内にある様々なIDを型安全に扱えるようDecodableでdecodeする」でした。
私の担当しているiOSアプリは、先日ついに自動テストを導入できました!
テストを書いたことないエンジニアが自動テストを導入するまでの格闘(?)を、ここに書き記したいと思います。
登場人物
私
- iOSアプリエンジニア(3年目)
- エンジニア歴=社会人歴
- 機能追加はSwiftで開発
- テストは業務で全く書いたことなし
アプリ
- ニフティ不動産 賃貸物件検索アプリ iOS版
- ニフティ不動産アプリシリーズで、最も多くのユーザーが利用しているiOSアプリ
- 2013年リリースで5年目突入(リリース当時のコードは現役で動いている)
- リリース当時はフルObjective-C、現在は一部Swift化
- 沢山のユーザーに使っていただけている反面、さまざまな機能があるためコードは複雑になっている
自動テストを導入しようと思ったきっかけ
今まで
今まではテストは全て手動で行っていました。
そのため以下のようなことがよく発生していました。
- 起こりうるテストケースを全て書き出す(書き出した人によって粒度が異なる)
- エラーのテストは、どうにかしてエラーの状況を作り出してテストをする
- 気づいたらテストの量が膨大になっていて、泣く泣く残業して黙々とiPhoneを操作し続ける
- アプリ自体も結構古く、複雑になっているため、一箇所変更するとどこまで影響するのか謎
自動テストを導入することはこれらを解決できることは認識していたものの、
このアプリのような古く、複雑なアプリに導入するのは難しいだろうと思っていました。
iOSDC2018 に参加
iOSアプリカンファレンスのiOSDC2018 やそのアフターイベントに今年初めて参加しました。
iOSDC2018の内容に関しては別の記事にまとまっておりますのでよかったらご覧ください。
初めて他の会社の開発環境や取り組んでいることを目の当たりにし、「テストを書くこと」の必要性を強く感じました。
また、どのプロジェクトも楽に自動テストを導入したわけではなく、少しずつ自動化の障壁を無くしていったことも知れました。
iOSDCに参加した後、うちのアプリにも自動テストを入れてみよう!と決めました。
まずテストできる環境にしてみた
XCTestを導入
XCTestとは、iOSのユニットテストのフレームワークです。
プロジェクトを作成すると、デフォルトで作成されます。
(このアプリには無かったので追加しました)
[iOS] ユニットテストの始め方 〜 テスト環境の作り方とXCTestの使い方 〜 を参考に
特に躓くことなく導入できました。
自動テストの環境構築は、どんなアプリでもすぐにできそうです!
書いてみる
今回は、この画面に関するテストを行いました。
画面に複数ボタンを配置し、それぞれボタンをタップすると正しい画面に遷移するかというテストを書きました。
設計はVIPERアーキテクチャを採用しました。
詳しい説明はVIPERアーキテクチャ まとめ という@hicka04 さんの書いた記事があるのでご覧ください。
プロダクトコードの説明は省きますが、おおまかに以下のような処理を行っています。
- presenterはviewControllerからのボタンのイベントを受け取っているクラス
- routerは次の画面への遷移を制御しているクラスです。
- presenterのtapOptionButtonというメソッドを呼んだとき、buttonTypeによって、routerからそれぞれ正しいメソッドが呼ばれるかをテストしています。
※実際のプロダクトコードとは異なります
※テストの書き方は、調べるとサンプルコードや技術ブログなど沢山出てくるので、そちらを参考に書いてみて下さい。
(今回はあくまでテスト初心者が書いたものです)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
func test_tapOptionButton() { XCTContext.runActivity(named: "正しい画面に遷移するか") { _ in XCTContext.runActivity(named: "駅選択画面") { _ in self.presenter.tapOptionButton(buttonType: .station) XCTAssertTrue(router.isCalled_showStationSelect) } XCTContext.runActivity(named: "市区町村選択画面") { _ in self.presenter.tapOptionButton(buttonType: .city) XCTAssertTrue(router.isCalled_showCitySelect) } } } |
また、Xcode9からXCTContext.runActivity(named:)というメソッドが使えるようになりました。
これを使って一つの処理を囲むことにより、テストコードが構造化され見やすくなります。
またテスト結果も、どのテストが失敗になっているかが見やすくなります。
テストが成功しました~!!
自動テストを導入するに当たって決めたこと
簡単には理想の環境にはならないことは明らかだったので、以下のことを決めました。
- 既存の機能にテストは書かないこと
- テストを書きやすい設計で実装するために、設計レビューをすること
- まずはテストを書いてみること
既存の機能にテストは書かないこと
今までのコードにテストを書くのはかなり労力と工数がかかることは明らかであったため、今までのコードに対する自動テストは書かないことにしました。
その代わり、新たに実装する機能にはテストを書くことを徹底することにしました。
現在少しずつですがObjctive-CからSwiftへ書き換えているので、手を入れるタイミングでテストを書いていこうと思っています。
テストを書きやすい設計で実装する
テストを導入している別プロジェクトのアプリではVIPERという設計思想で書かれていたため、社内でテストの知見があるVIPERの設計を使って実装を行いました。
まず必要なメソッドを書き出してプロトコルとして先に列挙してしまい、それをまずレビューしてもらうことにしました。
これにより、このメソッドに対するテストを書けば良いのかということが明確になりました。
まずテストを書いてみる
何はともあれ、まず最初のテストを書いてみないと何も始まらないです。
全てのテストが網羅できなくても、まず一つでも書いてテストを成功させてみることを目標としました。
一つテストが成功すると、嬉しくてもっと書こうという気持ちになりました!
まとめ
別アプリで自動テストを導入した先輩やチームメンバーにレビューをしてもらったりして、ようやく自動テストが自分の担当アプリで動くようになりました。
まだ大半のコードにテストが書けていない状況ですが、少しずつテストを書いていき、安全に開発できるアプリの環境を作っていきたいなと思っています。
また、うちのアプリもコードやばくてテスト入れられないよ〜と悩んでいる方は、
この通り古めのアプリでも、少しずつなら導入できるよということを感じていただけたら嬉しいです。
ぜひ、周りを巻き込んで挑戦してみてください!
明日は @omasumasu さんです!お楽しみに〜