こんにちは。Yahoo!フリマでAndroidアプリの開発を担当している惣元です。
Yahoo!フリマはCtoCのフリマサービスです。「モノの価値を最大化できるプラットフォームの提供」をミッションとして、「モノと人や情報を結ぶことでモノを最大限楽しみつくす社会の実現」を目指しています。サービスの大きな特徴として、動画出品、グッズ交換機能など、他のフリマサービスにはない独自の機能も提供しています。
今回は私たちの開発チームで実際に運用している、CI(継続的インテグレーション)による単体テストについて、実行時間を1時間から20分に減らせた工夫をご紹介します。
アプリリリースまでの開発フロー
Yahoo!フリマ Android開発チームでは開発手法として「Lean XP」を取り入れており、2週間のイテレーションでアプリをリリースすることで、「ユーザーに価値のあるものを素早く届ける」ことを実現しています。2019年10月のサービスローンチ以来、180回以上アプリをリリースしており、新機能の追加や品質改善を継続的に行っています。
このような素早いスピードでアプリをリリースするためには、開発フローにおける以下の各開発フェーズをスムーズに進めることが重要です。
- 企画立案:ユーザーのニーズや市場の動向を分析し、新しい機能や改善点を提案
- 仕様設計:企画立案で決定した内容を具体的な仕様として設計
- 実装:仕様に基づいてアプリを開発
- QA(品質保証):実装されたアプリが仕様通りに動作するかを確認
開発チームで抱えていた課題
アプリのリリースを重ねることで新機能の追加が行われてきましたが、それに伴いアプリの複雑さも増加しており、開発・保守の難易度が上がっていました。サービスローンチ時はフリマアプリの基本的な機能が約60画面で構成されていましたが、2024年7月現在ではさまざまなユーザーのニーズに応えるために機能追加・改善を繰り返し、約240画面と4倍近い画面数で構成されています。
また、機能実装とともにアプリの品質を維持するための単体テストも実装しています。単体テストのテストケースは4,700件を超えており、これによりテストの実行時間も増加しています。
開発ではソースコードをレポジトリにプッシュするたびにCIによる単体テストを実行することで、アプリの品質をチェックしています。単体テストで問題が発生していないことを確認した後に、アプリをQA担当者へ配布し、機能要件・非機能要件の基準を満たしているか確認を行っています。4,700件を超える全テストケースを実行するためには1時間程度必要となり、エンジニアがソースコードをプッシュしてからQA担当者へアプリを提供するまでの時間もかかるようになっていました。
これにより、開発フローのスピード低下が課題となっていました。
早くアプリの品質をチェックするメリット
単体テストはプログラムの修正・変更を行った際に、該当箇所以外へ影響を及ぼしていないかを確認するためのリグレッションテストとして活用しています。エンジニアがプログラムを変更してから早い段階で問題に気づくことができれば、変更箇所の状況をより把握した状態で修正に着手できるため、少ない工数で修正することが可能です。
そこで直近のソースコード差分に着目して、変更が発生している箇所だけ単体テスト(差分単体テスト)を実行することにより、単体テ ストの品質を維持した状態で実行時間の短縮化を図ることとしました。
差分単体テストの運用方針
差分単体テストを実施するためにはプロジェクト全体で単体テストを実行するのではなく、部分的に単体テストを実行できることが必要です。Yahoo!フリマ Androidアプリはマルチモジュールで構成され、以下のような単位でのモジュール分割を行っています。
- app: アプリのエントリーポイント
- feature: 各機能(商品検索、出品など)単位の管理
- repository: 複数のfeatureから利用される情報
- core: ログイン、カメラなどのさまざまなモジュールから利用される機能のライブラリ
アプリがマルチモジュール構成となっているため、モジュール単位で単体テストを実行することが可能です。またモジュール間が疎結合であるため、1つのモジュールを変更しても他のモジュールに影響を与えにくくなっています。そこで、ソースコードの変更が行われた箇所をモジュール単位で抽出することにより、テスト対象モジュールを選定し、テストを実行する方針としました。
また、アプリ開発ではGit-flowによりソースコードを管理しています。そこで、各ブランチごとにdevelopブランチの分岐元と最新のコミットの変更を対象範囲として処理を実行しています。また、差分単体テストだけでなく、1日1回単体テストの全テストケースを実行することで、アプリ全体の品質に問題がないことも併せて確認しています。
以下は、fastlaneで実行する場合の実装例です。
lane :partial_branch_unit_tests do
# 分岐元のdevelopブランチから差分のあるモジュール名を取得
modules_output = sh("git diff --name-only origin/develop | awk -F'/' '{print $1}' | sort | uniq")
# 単体テストが存在するモジュールでフィルタリング
modules_output = modules_output.split("\n").filter { |module_name| ["app"].include?(module_name) || module_name.start_with?("feature_", "repository_", "core_") }
# 該当のモジュールの単体テストを実行
tasks = modules_output.map { |module_name| "#{module_name}:testDebugUnitTest" }
if !tasks.empty?
gradle(tasks: tasks)
end
end
変更したモジュールの数に依存しますが、約20分で差分単体テストが完了するようになりました。全テストケース実行時は1時間程度必要だったため大幅な実行時間の短縮を図ることができました。
おわりに
今回は、Yahoo!フリマ Android開発チームで実際に運用している、CIによる差分単体テストについてご紹介しました。私たちのチームでは、アプリの品質を高めるために、効率的な開発プロセスを日々検討しています。また、現状の課題点をどのように解決・改善するかをチーム内で相談し合いながら、エンジニア自身が開発しやすい環境づくりを目指しています。この記事が少しで も皆様のお役に立てば幸いです。
また、Yahoo!フリマでは一緒にサービスを開発するエンジニアを募集中です。ご興味を持っていただけた方・一緒に開発してみたいと感じていただけた方。ぜひ、一緒にYahoo!フリマのサービス開発をしてみませんか?