これはHubble Advent Calendar 2025の4日目の記事です。
はじめに
こんにちは、Hubbleでバックエンドエンジニアとして業務委託で参画している山内と申します。
Hubbleは本年にシリーズBを終了し、スタートアップとして更にサービスが伸びていく過程です。
組織の拡大や機能開発も更に活発になっていく中で、中長期的な目線で品質の担保と開発スピードを確保していくことが求められます。
Hubbleが今後もより多くのユーザーに「高品質な」サービスを「早く」提供していくためにも、バックエンドのテストの重要性が大きくなってきており、今回はバックエンドのテスト周りで生じていた課題とアプローチ、取り組みの成果と今後の展望をお話しします。
前提として、Hubbleのバックエンドは主にRuby(Ruby on Rails)による開発が多くを占めており、テストコードはRSpecで書いています。
バックエンドのテストの重要性とは
「不具合の多くはバックエンドの結合テストで捕捉できる」
これは私がエンジニアとして、いくつかサービスを開発してきた中で得た経験則ではありますが、フロントエンド/バックエンド/インフラとレイヤーを分けて考えた時に最も不具合が出やすいのはバックエンドという考えがあります。
当然、それぞれのレイヤーで担保するべき内容は異なりますが、バックエンドは特性上、データベース(以下、DB)の状態を守る必要があり、不具合の直接的な原因はデータの不整合によるものが多いと考えています。
設計も共通のインタフェースをもとに行うことも多く、また一つの機能でDBの複数テーブルに及ぶ更新や取得が多くなりがちで、ロジック間を通した結合レベルの不整合が発生しがちです。
実際、障害が発生した際に原因を振り返ると、「それって結合テストで防げたのでは?」といった話が出ることも多いと思います。
サービスが成長してくると、「結合部分でのバグ」が目立ってくるので、バックエンドのテストでロジックの結合箇所の動作担保の重要性が増します。
テスト周りで生じていた課題
大きく以下の3点に課題がありました。
- テストの抜け漏れの確認のしにくさ
- モックが多く、結合テストとしての機能性の低さ
- テスト内容の不十分さ
以下、それぞれに関して詳述します。
テストの抜け漏れの確認のしにくさ
私がJOINした2024年10月の時点で、Contorller/Service/Model/Serializerと主要な層のテストは書かれていました。
ただ、日々開発を行う中でプルリクエスト(以下、PR)単位で、変更に対するカバレッジを把握する仕組みがなく、以下のような状況が発生していました。
- テストが未実装の行でバグがあり、本番リリース後に発覚
- テストコードはあるものの、テストの実装が不適で意図した通りの検証になっていないことが多かった
- 結果として該当ロジックの行がカバーされていない状態だった
- テストがあると思ってリファクタリングしたが、バグがあることに気づけずリリースしてしまうことがあった
- コードレビュー時に変更に対するカバレッジがすぐに把握できなかった
- PRは可能な限り小さくする、は開発組織全体として共通認識を持っているが、「変更行のテストは書かれているか」を意識しながらレビューする必要があった
- 先に変更に対するカバレッジがわかれば、レビュー観点をテストケースや書き方に集中しやすくできるはず
上記より、カバレッジをPR単位で確認できる仕組みを検討することになりました。
モックが多く、結合テストとしての機能性の低さ
テスト対象のメソッド以外のロジックをモックする考え方は割と一般的だと思います。
ただ、肝心のロジックがモックされていることにより、テストはあって通っているのに不具合が出ている状況がよく見られました。
特にControllerやServiceといった上位層でモックが多く、結合テストとして機能しているとは言い難い状況でした。
テストの容易さや実行速度等、モックにメリットはあるものの、私は「外部サービスや外部APIとの接続部を除いて、可能な限りモックは控えた方が良い」と考えています。
この考えに対する意見として、よく以下の点が挙げられます。
「他のロジックはそこのテストで担保しているのだから、モックしても良いのでは?」
「責務の観点から対象のロジックのみテストすれば良いのでは?」
このように聞かれた時に私は以下のように回答します。
-
対象のメソッドが「外から呼ばれた時に期待する動作(取得・副作用)を検証する」のがテスト
- Aというメソッドの中で他のメソッドa, bによるDBの更新があれば、外から呼んだ時に期待されるのは「a, bを呼ぶ事」ではなく、「a, bによって行われる更新」のはず
-
モックすると、ロジック間のデータの受け渡しが検証できなくなってしまう
- そもそも、バグはロジック間の界面で起こることが多い
- モックは型・値ともに自由に設定できてしまうため、本来あり得ないデータによる入力でもテストが通ってしまう
-
モックすると、リファクタリング等により呼び出し先が変化しただけで失敗するテストになってしまう
- そもそもリファクタリングとして機能しない状況になる(コードを変更しても、動作に影響がないことを検証するのがテストのはず)
このように同一リポジトリ内のロジックのモックは品質担保能力を欠いてしまう要因になります。
チーム間で綿密なI/Fを敷いているような体制ではない限り、モックは控えた方が良いと考えます。
特にサービスが伸びてくると、結合部の不具合が目立ち始め、また複数のチームで同じコードを改修していくため、結合部の動作担保は「早くコードを信用できる状態」に持っていきやすく、結果として、機能追加・改修、リファクタリングのスピードの確保に繋がります。
上記より、同一リポジトリ内で完結するロジックはモックせずに結合テストをしていくことが大切です。
テスト内容の不十分さ
前述のようにバックエンド組織としてテストを書く習慣はあったものの、テスト内容がrequest specではステータスコードとレスポンスの簡易的な検証のみだったり、ServiceやModelのテストも戻り値を検証するのみの箇所が多く存在していました。
たしかに最低限の動作検証はできているものの、特にリソース更新系のロジックは改修時のデータ不整合を防ぐために、更新対象のModelおよびその関連Modelの更新をfield単位で行えるとベストと考えます。
field単位で検証することにより、ロジックを改修する際に影響範囲が不適なデータの状態になることをCIで検知できる点や、機能開発時に仕様の不整合等に気づくきっかけにもなります。
また、プライベートメソッドを単体でテストしており、それを使用するパブリックメソッド側ではテストしていないといった状況もよく見られました。
先述の通り、「外部(クラス, APIではクライアント)から呼ばれた時の動作を検証すること」がテストのため、プライベートメソッドはパブリックメソッド側で担保するのが理想の在り方です。
また、外部から見たときの動作を検証するという考えをベースとすると、内部実装が変わるとテストが落ちるというのはおかしいことになります。
上記のような点が組織としてきちんと言語化できておらず、そもそも、バックエンドのテストとして何をテストするべきなのかの共通認識を持つことが求められると感じました。
課題に対するアプローチと成果
カバレッジ計測ツールの導入
まず、PR単位のカバレッジの可視化から開始しました。
導入したツールはカバレッジ計測ツールとして有名なCodecovです。
Codecovは私も以前から経験があったため、第一候補として挙げ、結果として採用されましたが、技術選定は時間をとって行いました。
まず、以下をPRごとにチェックできることを要件として設定しました。
- PRごとに変更差分のカバレッジチェックができること
- テスト未実行の変更行チェック
- 全体カバレッジ増減チェック
設定が簡単で、ある程度枯れており、事例が豊富で調べやすいことも大切だと考えました。
また、横展開のしやすさも意識しました。
ビジネス展開として、中・長期的にRailsが中心になるものの、もし他言語のリポジトリにテスト追加したい・将来的にマルチプロダクト展開する場合でも対応しやすいこと、またフロントエンド(TypeScript)でも必要であれば使えると、組織としてベストな選定ができると考えました。
Codecovは上記をいずれも満たせると考え、改めて選定候補として提案しました。
そして、Codecovを導入し、PRごとに変更に対するカバレッジと全体カバレッジへの影響を出力し始めました。

以前は漏れてしまっていたり、リリースを優先してテストを実装しなかった等でカバーできていなかった箇所がありましたが、PRごとにカバレッジを出力することにより、カバーされていない箇所の意図が明確になりました。
未カバーの箇所があっても、レビュイーが意図を書くことでレビュアーも把握しやすくなります。
また、課題感のあったテスト未実装の箇所(テストはあるものの意図した動きになっておらず実質、未カバーの状態も含む)の検知が容易になり、新規実装で未カバーなことが原因の不具合は見られなくなりました。
導入から半年が経過すると、導入時から全体のカバレッジが約78%→86%に上がりました。

Hubbleは既にユーザーに価値を提供しているサービスであり、カバレッジを上げることが目的ではないものの、バックエンドメンバーが各自、日頃の開発でテストを大切にしている姿勢の表れでもあります。
テストガイドラインの作成と運用
次に、モックの多用による結合テストの機能性への影響とテスト内容に関する対応として、テストガイドラインを作成することにしました。
作成に至った経緯としては、週次のバックエンドメンバー全体の相談会において、テストのあり方に関して啓発した時のメンバーの反応が良く、基準として設定できるとメンバー皆のテストの書きやすさに寄与しそうだと感じたことでした。
ガイドラインを作成する際は、メンバーの能力や創意工夫を抑制するものにならないように意識しました。
具体的すぎたり厳密すぎるルールは逆にメンバーの能力を発揮しにくくしてしまいます。
ガイドラインはあくまでも、「全体としての水準を上げるもの」であるべきなので、「これだけは守ろう」というポイントの明確化に留め、メンバー各々がテストについて考えられる余白も残すように意識しました。
策定した内容の概要は以下の観点です。
- 単体テストは分岐網羅を満たすように書く
- モックは外部サービスや外部APIの接続部に留める
- プライベートメソッドはそれを使用するパブリックメソッドでテストする
- Rake taskやService、Modelはテスト対象のメソッドについて、アプリケーション上、想定し得る入力値のパターンを網羅し、それに対しoutputや副作用(DB, ファイルストレージの更新や非同期Jobのenqueue)が適正に行われることを検証する
- Controller(request spec)はユースケースに沿ったI/Oが適切であることを検証する
- 一覧取得系…欲しいデータのみが、意図した順番で返ってくるか
- 単一取得系…欲しいデータが返ってくるか
- リソース更新系…リソースの更新が意図通りにされているか
テスト内容の言語化 × 先述のカバレッジが確認できる仕組みの整備により、テストコードの書きやすい環境を整備した形です。
そして、運用フェーズの初期はCTOに相談の上、各チームのテストコードのレビューに入りました。
ガイドラインをもとにメンバーそれぞれが行動に移しやすくするためには、実際に書いてもらったものをレビューするタイミングが最もアドバイスしやすいと感じたためです。
(それぞれのチームの機能開発のスケジュールへの悪影響にならないように、そしてメンバーのモチベーションも大切にしながら行うことは前提として意識しました)
数ヶ月間レビューしていると、徐々にメンバー同士のテストコードに対するレビューが増えていきました。
コードレビューは焦点が実装部に集中しがちで、テストは書かれていればOKとなりがちですが、テストコードも観点や書き方のレビュー対象になってきました。
「ここはモックになっているから使わずにテストしよう」
「このような入力ケースもテストした方が良いのでは?」
「前のケースと重複したテストになっていないか?」
このようなレビューがメンバー間で段々できるようになっており、メンバーそれぞれの目が研ぎ澄まされてきており、バックエンド組織としてレベルアップし始めているなと実感しています。
実際にガイドラインをもとにしたテストコードにより、リリース前に仕様同士のコンフリクトに気付けたこともありました。
まとめと今後の展望
カバレッジの可視化とテストガイドラインを策定することで、テスト漏れの防止と効果的なテスト作成を行いやすくする環境が以前よりも整ってきました。
私が参画する前は一時期「テストは後回しにしてリリースする方が早いのではないか」といった時代もあったようですが、現在はメンバーそれぞれが「今の頑張りは今後の品質と開発スピードを守ることにつながる」「自分たちのプロダクトを守っていこう」という意志を持ちながら開発していると感じます。
品質を守るのみならず、チームが自信を持って進むための安全網となり、早期に信用できる状態を作るというテスト本来の目的に近づいているように思います。
メンバーのテストに対する意識の高さを語る関連記事もあります。
まだ完成系ではなく、テストコードのレビューに入る中で見えてきた新たな課題として、テスト観点や可読性・保守性の高いテストの書き方をバックエンド組織として強化していくことや、マイクロサービス間の連携のテストなど、より良くできる所はまだあるのでこれからも改善を続け、サービスの成長を支える土台作りをしていきたいと考えています。
今回の記事が少しでもお役に立ち、また、Hubbleの開発に興味を持っていただけたら幸いです!
Hubbleはメンバー皆がお互いを尊重しながら、良いものはどんどん取り入れていくカルチャーなので、日々ワクワクしながら開発できる楽しさがあります!
明日は、QA Lead Engineer 兼 Assistant EMの @miney さんです!

