understanding-the-testing-pyramid について
Android のテスト設計向けに、small・medium・big のスコープ分類と、5 段階モデルとの違いを解説するスキルです。テストのスコープと実行場所を区別し、テストピラミッドの定性的な指針や 70/20/10 比率の出典も説明します。
主な機能
- 01small・medium・big の分類
- 02スコープと実行場所の区別
- 033 段階・5 段階モデルの比較
- 04定性的なピラミッド指針の整理
- 0570/20/10 比率の出典の説明
活用シーン
- 01Android テストスイートの計画
- 02ローカルテストと実機テストの分類
- 03遅いスイートやスコープ混同の検討
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ skydoves/ android-testing-skills --skill understanding-the-testing-pyramidインストール後、Claude Code で/understanding-the-testing-pyramidを入力して読み込まれたか確認します。
詳細
テストピラミッドを理解する — 3 層のフレーミングを選び、一貫して使う
Android のテストスイートは、チームが各動作につき大規模なインストルメンテーションテストを 1 つだけ作成し、基盤となるロジックのユニットテストを省くと、CI が遅くなり、陳腐化します。Google の /training/testing/fundamentals ページでは、テストの規模を small、medium、big の 3 つの範囲で捉えており、/strategies ページではその上に定性的なピラミッドを示しています。このスキルでは 3 層のフレーミングを定義し、作成者が混同しないよう別の 5 層のフレーミングにも触れ、よく知られた数値比率の出所についても正直に説明します。後続のスキル(../choosing-what-to-test/SKILL.md、../../strategies/applying-testing-strategies/SKILL.md)では、この用語を前提としています。
このスキルを使う場面
- ユーザーが「ユニットテストと UI テストはそれぞれいくつ書けばよいですか」と尋ねたり、70/20/10 や 80/15/5 の比率を引用して Google のドキュメントに記載されているか尋ねたりする場合。
- ユーザーがあらゆるテストを「インテグレーションテスト」や「エンドツーエンドテスト」と呼んでおり、エージェントがテストを分類するための用語を必要としている場合。
- ユーザーが「CI が遅い」「テストスイートに 40 分かかる」と報告し、インストルメンテーションテストを削除すべきか知りたい場合。
- ユーザーが Robolectric は「ユニットテスト」なのか「インテグレーションテスト」なのか尋ねる場合 — Google のフレーミングでは medium-local テストです。
- ユーザーが新しいモジュールを始めるにあたって、どこから着手すべきか尋ねる場合(回答:
/strategiesに従い、まず small テストから始めます)。
このスキルを使わない場面
- ユーザーが各テストに何を含めるべきか(どの画面や ViewModel を対象にすべきか)を知りたい場合 —
../choosing-what-to-test/SKILL.mdを使います。 - ユーザーが Gradle の
testImplementationとandroidTestImplementationの設定を使い分けたい場合 —../../strategies/organizing-test-source-sets/SKILL.mdを使います。 - ユーザーが
Fake/Mock/Stub/Spyのどれを使うか選びたい場合 —../../doubles/picking-test-doubles/SKILL.mdを使います。 - ユーザーが 1 つの不安定なテストをデバッグして修正したい場合 —
../../../compose/synchronization/synchronizing-with-idle/SKILL.mdまたは../../../jvm-tests/coroutines/testing-coroutines-with-runtest/SKILL.mdから始めます。
前提条件
- 議論を具体的にするため、少なくとも 1 つのテストクラスがあるモジュール。
src/test/(ローカル、JVM)とsrc/androidTest/(インストルメンテーション)の分割についての知識。よく知らない場合は、まず../../strategies/organizing-test-source-sets/SKILL.mdを読んでください。- Google によると、「スコープ」(small/medium/big)と「実行場所」(ローカル/インストルメンテーション)は独立した 2 つの軸であるという理解。
2 つのフレーミング(スキルや設計ドキュメントごとに、必ずどちらか 1 つを選ぶ)
Google は隣接するページで 2 つのフレーミングを公開しています。両者は矛盾しているのではなく、異なる見方を示しています。
フレーミング A — Small / Medium / Big(3 層) ← このスキルではこちらを推奨
/training/testing/fundamentals(「Types of tests in Android」→「By Scope」)より:
「テストは、スコープ、つまりカバーするコードの範囲によっても分類されます。テストスコープには次の 3 つのカテゴリがあります。
- Unit tests または small tests は、メソッドやクラスなど、アプリのごく小さな部分だけを検証します。
- End-to-end tests または big tests は、画面全体やユーザーフローなど、アプリのより大きな部分を同時に検証します。
- Medium tests はその中間に位置し、2 つ以上のユニット間の統合を確認します。」 —
developer.android.com/training/testing/fundamentals
| スコープ | 対象範囲 | 一般的なランナー | 一般的な実行環境 |
|---|---|---|---|
| Small | 1 つのメソッドまたはクラス | JUnit4 + Mockito/MockK + fakes | src/test/(JVM) |
| Medium | 2 つ以上のクラスの統合。Robolectric を介して Android フレームワークを使用する場合もあります | JUnit4 + Robolectric またはプロセス内 Android | src/test/(ホスト)または src/androidTest/(インストルメンテーション) |
| Big | 画面全体、ユーザーフロー、またはリリースビルドのスモークテスト | AndroidJUnitRunner + Espresso/Compose-test/UiAutomator | src/androidTest/ |
フレーミング B — ユニット/コンポーネント/機能/アプリケーション/リリース候補(5 層)
/training/testing/fundamentals/strategies(「スケーラブルな戦略」テーブル)より:
| レベル | スコープ | ネットワーク |
|---|---|---|
| ユニット | 依存関係を最小限に抑えた単一のメソッドまたはクラス | なし |
| コンポーネント | モジュールまたはコンポーネント | なし |
| 機能 | 複数のコンポーネント/モジュール | 「モックしたネットワークアクセスをサポート」 |
| アプリケーション | アプリ全体、デバッグ可能なバイナリ | 該当なし |
| リリース候補 | リリースビルド全体、最小化済み | 該当なし |
ほとんどのチームは、よりシンプルでよく引用される形であるフレーミング A の 3 層構成を使い続けています。フレーミング B を採用するのは、粒度を増やす価値がある複数チームの CI 戦略を設計する場合に限ってください。絶対に 1 つのドキュメント内で 2 つを混在させてはいけません。一方を選び、それに統一してください。
スコープと実行場所は直交します
ページ上で最も重要な引用であり、開発者が「私のテストはすべてインストルメント化されています」と言ったときにエージェントが引用すべきものです。
「すべてのユニットテストがローカルで実行されるわけではなく、すべてのエンドツーエンドテストがデバイス上で実行されるわけでもありません。たとえば、次のようなものがあります。
- 大規模なローカルテスト:Robolectric のように、ローカルで実行される Android シミュレーターを使用できます。
- 小規模なインストルメント化テスト:SQLite データベースのようなフレームワーク機能を使って、コードが適切に動作することを検証できます。」 —
developer.android.com/training/testing/fundamentals
2 つの軸:
LOCAL (JVM) INSTRUMENTED (device)
SMALL pure JUnit + fakes unit test of SQLite via real DB
MEDIUM Robolectric component component test on real Android
BIG Robolectric flow Espresso / Compose UI flow
Robolectric はローカルの Android シミュレーターです。「インストルメント化テスト」ではありません。src/test/ で JVM 上で実行されます。../../strategies/organizing-test-source-sets/SKILL.md と、カテゴリをまたぐスキル ../../../jvm-tests/robolectric/using-robolectric-correctly/SKILL.md を参照してください。
定性的なピラミッド
/strategies ページが示しているのは、定性的な形だけです。
「ほとんどのアプリでは、小規模なテストを多く、大規模なテストを比較的少なくし、ピラミッド型にする必要があります。」 —
developer.android.com/training/testing/fundamentals/strategies
「**重要なポイント:**一般的に、開発サイクルのできるだけ早い段階でテストを追加するようにしてください。通常は、小規模なテストから始めることになります。」 —
developer.android.com/training/testing/fundamentals/strategies
これが、Google が現在、定量的に述べているすべてです。「小規模なテストを多く、大規模なテストを少なく」。比率は示されていません。
70/20/10 の比率の実際の出典
「ユニットテスト 70%、統合テスト 20%、エンドツーエンドテスト 10%」というルールは広く引用されていますが、現在の /training/testing/fundamentals ページにも /training/testing/fundamentals/strategies ページにも記載されていません(2026-05-06 時点。tasks/research/R8-android-fundamentals.md で確認済み)。この数字の出典は Google 自身の技術文献である Software Engineering at Google の第 11 章であり、Android のトレーニングページではありません。
「70/20/10」を developer.android.com の記述として帰属させてはいけません。この数値比率は、Software Engineering at Google(https://abseil.io/resources/swe-book、第 11 章)で広まった、コミュニティ内の経験則です。慎重に言い換えてください。書籍の該当箇所を一行ずつ確認しない限り、特定の文を逐語的な引用として示してはいけません。この書籍では比率を法則ではなく経験則として扱っています。分野によって適切な比率は異なります。以下の「ピラミッドを崩す場合」を参照してください。
コストと速度のトレードオフ
ピラミッドの底辺が広い理由は次のとおりです。
| レイヤー | テストごとの実時間 | 失敗シグナル | メンテナンスコスト |
|---|---|---|---|
| 小規模 | 1-50 ms | クラス/メソッドを特定します | 低 |
| 中規模(Robolectric) | 50-500 ms | コンポーネントを特定します | 中 |
| 大規模(インストルメント化) | 5-30 s | 「画面が壊れています」と示します | 高(不安定さ、エミュレーターの状態、アニメーション) |
平坦または逆ピラミッド型(大規模テストが多く、小規模テストが少ない構成)にすると、次のようになります。
- CI が遅くなります(カバレッジが同等のユニットテスト中心のテストスイートより10倍から100倍遅くなります)。
- 不安定なテストが増えます。不安定さはテストの規模や外部依存関係と相関します(
/strategiesでは、ユニットレイヤーとコンポーネントレイヤーについて「ネットワークアクセスなし」と記載されています)。 - 失敗シグナルが曖昧になります。「画面Xが壊れています」ではなく、「入力Zに対して関数Yが誤った値を返しました」と示せます。
- リファクタリングが大変になります。UI テストはレンダリングされたテキストを検証するため、文言を変更するたびに失敗します。
/training/testing/instrumented-tests では、コストが明示されています。
「実デバイスの動作に対するテストが必須の場合にのみ、インストルメント化テストを使用することをおすすめします。」 —
developer.android.com/training/testing/instrumented-tests
これは、Google自身の言葉による「大規模テストは最終手段として使う」という姿勢です。
意図的にピラミッドを崩す場合
ピラミッドはデフォルトであって、絶対的なルールではありません。次の場合は崩してもかまいません。
- プロダクトそのものがインテグレーションである場合。 主に3つのベンダー SDK を連携させる決済 SDK では、グルーコードのユニットテストより、機能/アプリケーションテストのほうが価値があります。ユニットテストでは、モックの呼び出し検証を同語反復することになります。
- ロジックをフレームワークが担う場合。 DataStore を使う
ViewModelと標準の Material ウィジェットで構成された、純粋な CRUD 画面には、ユニットテストの対象となるものがほとんどありません。大規模テストでは配線を検証できますが、小規模テストでは単にrunBlocking { dataStore.data.first() }をテストするだけになります。 - 決定性が重要な場合。 描画の正確性を検証するテストスイート(スクリーンショットテスト、アニメーションのタイミング)は、バグが現れる場所である大規模テストレイヤーに置きます。カテゴリー横断スキル
../../../compose/audit/auditing-compose-test-suite/SKILL.mdを参照してください。 - コストの経済性が逆転する場合。 Firebase Test Lab / Gradle Managed Devices では、大規模テストの分あたりのコストが十分に低く、いずれにせよメンテナンスコストのほうが支配的になる場合があります。それでも、ユニットテストのほうが不安定さを抑えられます。
ピラミッドを崩す理由にならないもの:
- 「ユニットテストのセットアップが難しすぎる」—これはアーキテクチャに問題がある兆候であり、大規模テストを増やす理由ではありません。
developer.android.com/training/testing/fundamentals(「テスト可能なアプリのアーキテクチャを使うと……」)を参照してください。 - 「X をモックできない」—モックの代わりにフェイクを使ってください。
../../doubles/picking-test-doubles/SKILL.mdを参照してください。 - 「自分のマシンでは動くので、デバイステストを増やしてそのままリリースしよう」—不安定さはテストスイート全体に広がります。
../../../adb/observability/extracting-logs-with-logcat/SKILL.mdを使って原因を特定してください。
パターン
パターン:誤りと正解 — 70/20/10 の比率を引用する
> Per Google's developer documentation, the testing pyramid recommends
> a 70/20/10 split between unit, integration, and end-to-end tests.
> Source: developer.android.com/training/testing/fundamentals/strategies
誤りの理由:2026-05-06 時点で、その比率は strategies ページに記載されていません。このページでは「小規模なテストを多数、大規模なテストを少数」と定性的に述べているだけです。ここで developer.android.com を引用するのは捏造です。
> Google's Android docs commit to a qualitative pyramid only:
> "Most apps should have many small tests and relatively few big tests"
> (developer.android.com/training/testing/fundamentals/strategies).
> The familiar 70/20/10 split comes from Software Engineering at Google,
> ch. 11 (https://abseil.io/resources/swe-book), not the Android docs.
パターン:誤りと正解 — Robolectric を分類する
> Robolectric tests are instrumented tests because they exercise the
> Android framework. Put them in src/androidTest/.
誤りの理由:Robolectric は、Android デバイスやエミュレーターなしで JVM 上で実行されます。/training/testing/fundamentals(「大規模なローカルテスト:Robolectric など、ローカルで実行される Android シミュレーターを使用できます」)によると、Robolectric はスコープにかかわらず、src/test/ に置くローカルテストです。
> Robolectric is a local (JVM) Android simulator. By scope it can be small,
> medium, or big depending on what the test exercises. Place it in src/test/.
> Use src/androidTest/ only for tests that require a real device.
パターン:誤りと正解 — 1つの設計ドキュメントでピラミッドを説明する
> Our suite has 200 small tests, 40 medium tests, 8 feature tests, and
> 3 release-candidate tests, which is a healthy pyramid.
誤りの理由:これはフレーミングA(「小規模/中規模」)とフレーミングB(「機能/リリース候補」)を混在させています。読者には、「小規模」がフレーミングAの小規模なのか、フレーミングBのユニットなのか判断できません。
> Our suite has 200 small tests, 40 medium (Robolectric component) tests,
> and 11 big (instrumented Compose-test + Espresso) tests. Per Google's
> small/medium/big framing on /training/testing/fundamentals, the ratio
> is roughly 80/16/4, in line with the pyramid recommendation.
判断マトリックス — 新しいテストのスコープはどうすべきか?
Question → Scope
----------------------------------------------------------
"Does it depend on the Android framework?"
No → Small (src/test/)
Yes, simulated via Robolectric → Medium (src/test/)
Yes, must be a real device → Big (src/androidTest/)
"Does it exercise more than one class?"
No → Small
Yes, but no I/O / no UI → Medium
Yes, with UI rendering or system services → Big
"Does it cross a network or process boundary?"
No → Small or Medium
Yes (mocked) → Medium (Feature in Framing B)
Yes (real) → Big (Application/RC in Framing B)
必須ルール
- 必須: デフォルトのスキル文面と設計ドキュメントでは、「小/中/大」の3層フレーミングを使用してください。5層の「単体/コンポーネント/機能/アプリケーション/リリース候補」フレーミングを使用するのは、
/strategiesページを明示的に参照する場合に限ります。 - 禁止: 1つのドキュメント内で2つのフレーミングを混在させないでください。どちらか一方を選んでください。
- 禁止: 「70/20/10」の数値比率を
developer.android.comに帰属させないでください。Software Engineering at Google(https://abseil.io/resources/swe-book)を引用してください。 - 禁止: Robolectric を「インストゥルメント化テスト」と表現しないでください。Robolectric は、あらゆるスコープで利用できるローカル Android シミュレーターです。
- 必須: 新しいテストにはすべて、コメントまたは命名規則(
androidx.test.filtersの@SmallTest、@MediumTest、@LargeTest)でスコープを明記し、CI でシャーディングできるようにしてください。 - 推奨: Google の「小さなテストから始める」というガイダンスに従い、新しいモジュールではまず小さなテストから始め、小さなテストでは対応できない場合にのみ、中規模/大規模テストを追加してください。
- 推奨: CI が遅い場合は、小さなテストを追加する前に、重複する大規模テストを削除してください。ピラミッドを引き締めるには、土台を広げるだけでなく、頂点を取り除きます。
検証
- モジュール内の各テストクラスが、
@SmallTest、@MediumTest、@LargeTestのいずれか、またはソースセットの配置によって分類されています。 -
src/androidTest/内に、精度を損なわず Robolectric のホストテストとしてsrc/test/で実行できるテストがありません。(該当する場合は移動してください。) - CI ログで、変更ごと、各レイヤーごとに、ユニットテストの実行時間がインストゥルメント化テストの実行時間より短いことが報告されています。
- 「70/20/10」を
developer.android.comに帰属させている設計ドキュメントがありません。 - Framing A と Framing B の用語を混在させている設計ドキュメントがありません。
- チームのテスト戦略ドキュメントの用語集に記載されているフレーミングは、ちょうど1つです。
参考資料
developer.android.com/training/testing/fundamentals— 小/中/大のスコープ、「すべてのユニットテストがローカルとは限らない」という引用、テスト可能なアーキテクチャの根拠。developer.android.com/training/testing/fundamentals/strategies— 定性的なピラミッド(「小規模テストを多数、大規模テストを少数」)、5段階の「単体/コンポーネント/機能/アプリケーション/リリース候補」表、「小さなテストから始める」というガイダンス。developer.android.com/training/testing/instrumented-tests— 「実デバイスの動作に対するテストが必須の場合に限り、インストゥルメント化テストを使用する」。- Software Engineering at Google、第11章 — 70/20/10 比率の出典。https://abseil.io/resources/swe-book
tasks/research/R8-android-fundamentals.md— 引用文の原文、バージョンに関する注記、「ページに70/20/10の記載はない」という調査結果。androidx.test.filters— 実行時のシャーディングに使用する@SmallTest/@MediumTest/@LargeTestアノテーション。- 関連スキル:
../choosing-what-to-test/SKILL.md、../../doubles/picking-test-doubles/SKILL.md、../../strategies/applying-testing-strategies/SKILL.md、../../strategies/organizing-test-source-sets/SKILL.md。 - カテゴリー横断:
../../../jvm-tests/robolectric/using-robolectric-correctly/SKILL.md、../../../jvm-tests/runner/configuring-junit4-on-android/SKILL.md、../../../instrumentation/runner/running-instrumented-tests-with-androidjunit4/SKILL.md、../../../compose/audit/auditing-compose-test-suite/SKILL.md。