こんにちは。2020年6月から Quipper に参加している @omtians9425 です。現在新規プロジェクトにおいて Android アプリ開発を担当しております。
今回は弊プロジェクトの iOS/Android チーム合同で行っている、ユニットテストのケースを相互にレビューし合う取組みについてお話します。
背景
弊プロジェクトのモバイルチームでは、プロジェクト開始当初からテスト方針について QA エンジニアと共に議論してきました。そこで、プラットフォーム間の品質・実装差分をなるべく減らすための取り組みとして、ユニットテストケースをお互いにレビューするのはどうか、というアイディアが出ました。
弊チームでは、各プラットフォームにおける適切なデザインや独自仕様の実装を除き、アプリの中核となるビジネスロジックについては iOS/Android で原則共通になると考えています。一方で言語やプラットフォーム、そしてチームが分かれている以上、意図しない実装差分が発生してしまうリスクが存在します。
そこで、本取り組みにおいてテスト対象となる仕様の再確認や、仕様に対するテストケースの妥当性の確認を行うことで、現時点での実装差分・テスト品質の差分を減らし、ひいては将来的な実装差分の発生を減らすことにも繋がるのではないかと考えました。
この記事では、具体的にどのようにクロスレビューを行っているかということと、現時点で認識しているメリット・課題や展望についてご紹介します。
実施方法
どのようにレビューを行っているかをご紹介します。
実施方針
実施の方針として、厳格なガイドライン・ルールを設けることはしていません。取り組み開始当初から適切なやり方を広く探っていくためでもありますが、あまり詳細に決めすぎても形骸化することが想定されるためです。
そのため以下のような共通認識を持つに留めました。
- テストコード自体のレビューはせず、仕様に対してどのようなテストケースを書いたのかをレビューする
テストコードのレビューは各チームの Pull Request レビューで担保し、ここではよりマクロなレベルで「何をもって仕様を正しく実装できていることを保証したのか」という観点でケースをレビューする、という考え方です。
従って、例えば iOS/Android でテストケースがアーキテクチャにおけるどの層に書かれているかなどは問わず、共通したケースが存在していれば良い、と考えます(適切な設計をしていないためにケースが書かれている層が変わってしまうなどは起こり得ますが、ここは各プラットフォームの責任範囲とします)。
具体的な実施方法
前述の通り詳細なルールは決めていないため担当者によって実施方法はそれぞれなのですが、よく行われている方法をご紹介します。
実施単位・粒度
弊プロジェクトでは issue の管理方法として GitHub Issues (+ ZenHub) を利用しており、そこに積まれた開発機能 issue (チケット) 単位で行っています。開発機能 issue は基本的には画面単位になることが多く、そこに紐づける形でクロスレビューの issue を、 iOS/Android の共通 issue として backlog に積んでおきます。
参加者
当該機能とテストケースの実装者が各プラットフォームから最低1人ずつ参加します。実装に直接関わっていないメンバーの参加は任意です。
レビュー方法
作成したクロスレビュー用の issue に、 iOS/Android の実装者がそれぞれ仕様に対して実装したテストケースを話しながら洗い出していきます。適宜 backlog などを参照しながら仕様を確認して行います。
以下は、サンプルアプリに対してクロスレビューをトライアルした時の例です。
# 仕様1. ポストデータ一覧が表示される - ポストデータ取得が成功した場合 - iOS: View に通知されるポストが ViewModel に割り当てられていること確認した (View に表示されたかまでは見ていない) - Android: 同じく、 ViewModel にある変数に値が割り当てられていれば View に表示されるだろうという前提の上で、その変数に正しくデータが割り当てられているかをチェックした - ポストデータ取得が失敗した場合 - iOS: エラーを表現するオブジェクトに値が割り当てられているかをテストした。エラー文言もテストした - Android: 文言のテストまではしていないが、それ以外は同一 ...
テストケースを整理することができれば良いためやり方は自由で、よりラフに付箋形式で整理しても良いかもしれません。
また実施時点の仕様に対するケースに過ぎないため、作成されたドキュメントを保存・管理することも想定しておらず、整理ができれば破棄します。お互いの実装イメージを掴んだ方がわかりやすい時には適宜ソースコードを見せ合うといったことも行います。
またここでいう「仕様」の粒度もプロジェクトによってまちまちかと思いますが、弊プロジェクトでは Product backlog に箇条書き形式で記載されていて、その粒度に従っていました。
実施タイミング
お互いのテスト実装後が基本ですが、実装進度の兼ね合いでテストケースを書く前にも事前整理的な意味合いで実施することもあります。
実施後
仕様差分やケース漏れが検出された場合は当該プラットフォームの担当者が各自実装し、 Pull Request を出します。
取り組み結果
取り組み開始から半年以上が経過していますが、結果として iOS/Android 間でお互いのケース漏れや実装差分を複数検出しており、ローンチに向けてリスクを軽減することに寄与していると感じています。
具体的には、以下のような価値が確認されています。
- iOS/Android 共に reviewer であると同時に実装担当者のため仕様の詳細を理解した上でケースレビューができ、 Pull Request よりも深いレビューとなった
- 各プラットフォームに閉じたチームを超えてレビューすることで安心感が得られた
- ケースの抜け漏れに加えて仕様の認識齟齬に気付く砦にもなり得る
- 片方がテスト困難な実装をしていた場合に、もう片方がテスト可能であったとき、可能な設計にできることを示してくれる
特に「お互いが実装者であるため他メンバーよりも深いレビューができる」という点は重要だと考えます。この価値を最大化するために、なるべく実装担当者が参加しかつ実装後鮮度の高い状態で実施することが重要になります。
また最後の点について、クロスレビューの過程で「片方が書いているケースをもう片方が書いていない」状況に遭いますが、主に以下のような理由が考えられます。
- テストできる状態でありながら漏れてしまった
- テスタブルな設計になっていない
- Framework の都合上テスト困難な場合 (片方の OS では Framework の都合でロジックが UI から切り離し辛い部分があり粗いケースになってしまう)
これらのどの場合に該当するか整理し、最後のケースのような場合でも本当に unit test 可能な状態にロジックを抽出できないかどうか見直すといったことを行い、より良い unit test の実装を目指しています。
ここでも片方のプラットフォームの実装をモブプロ形式で見せることで、より良い設計に気付くことができました。
また、前項でアーキテクチャによらずテストケースをレビューできれば良いと言及しましたが、弊チームでは iOS/Android 共に MVVM アーキテクチャを採用しているということもあり、結果として ViewModel といった共通の言語でロジックのレビューができるという形になっています。
課題・展望
メリットの一方で、以下のような課題があります。
1つ目は仕様の検討が漸進的に行われている・ドキュメント運用方法が変化していくこともあり仕様の記載場所が必ずしも Single Source of Truth になっておらず、一箇所に網羅的に記載されているとは限らないといった問題です。こちらについては、仕様策定からテスト項目設計まで担当しているQAエンジニアに直接参加していただくことで、資料に依存しすぎない形をトライしています。
2つ目に関しては、基本的に sprint 開始時に両 OS で開発アイテムを揃えているものの、実装進度によってタイミングがずれてしまう問題です。 sprint 開始時に実装 issue が棚卸しされた時点でテスト実装に先立って実施することで見通しを立てておく、またその段階で issue 作成・担当者 assign を行っておくことでテスト実装後に実施することを忘れないようにする、などの対策を行っています。
現時点でのトライで終わらず、今後も振り返りをしながら改善を進めていく予定です。
おわりに
弊チームでは、このようにプラットフォームの垣根を超えた活動を継続しています! こちらの記事を読まれて Quipper に興味をお持ちいただいた皆様、ぜひ Quipper に遊びに来ませんか? 以下 Wantedly ページよりお気軽にご連絡ください! https://www.wantedly.com/companies/quipper/projects