自動化

単体テストと結合テストはもう古い?Google流「テストサイズ」とは(Small/Medium/Large)

「テストサイズ(Test Sizes)」は、Googleが巨大なコードベースを高速に回すために整理したテストの分類軸です。単体テストや結合テストが「どこまでのコードを検証するか」で分けるのに対し、テストサイズは「テストの実行にどれだけのリソースを許すか」で分けます。両者は似て非なる別の軸で、ここを取り違えると分類がうまく機能しません。この記事では、Small / Medium / Large の定義を実際の制約に沿って整理し、Bazelでの具体的な設定値やCI/CDでの使い分けまでを一次情報にもとづいて解説します。

まとめ

  • テストサイズ=テストが使ってよいリソース(プロセス・スレッド・ネットワーク・実行時間)による分類。検証するコードの広さを表す「スコープ(単体/結合/E2E)」とは別の軸です。
  • Small=単一プロセス(多くは単一スレッド)で完結。sleep・I/O・ネットワークアクセスは不可。Medium=単一マシン内に収め、通信はlocalhostのみ可。Large=複数マシンにまたがってよい。
  • Bazelでは size 属性がデフォルトのタイムアウトと想定リソースを決めます(small=1分/20MB … enormous=60分/800MB、未指定はmedium)。
  • CI/CDでは高速なSmallを全コミットで、遅くフレークしやすいLargeはマージ前やナイトリーで、と実行頻度を分けるのが定石です。

テストサイズとは?「リソース」でテストを分けるGoogleの考え方

Googleは、数十億行規模のコードベースを1日に何万回もビルド・テストする環境を持ちます。テストの本数が膨大になると、遅いテストや不安定(フレーク)なテストがCI全体の足を引っ張るため、「そのテストの実行に何を許すか」をあらかじめ縛る仕組みが必要になりました。それがテストサイズです。

ここで重要なのは、テストサイズはスコープ(scope)とは独立した軸だという点です。Googleは「1行のコードを実行すること」と「それが期待どおり動いたと検証すること」を区別し、前者に近い“検証するコードの広さ”をスコープ、“実行に必要なリソース(メモリ・プロセス・時間)”をサイズと呼び分けています。両者は相関しますが一致はしません。たとえば単一のUIコンポーネントというスコープの狭いテストでも、実ブラウザが必要ならMediumサイズになりますし、逆に複数コンポーネントをまたぐ広いスコープでも、外部依存をすべてテストダブルに置き換えればSmallサイズで書けます。

「Small=単体テスト、Medium=結合テスト、Large=E2E」という単純な対応で語られがちですが、それはあくまで“よくある組み合わせ”にすぎません。サイズはリソース制約、スコープはコードの広さ、と切り分けて理解すると迷いません。

Small / Medium / Large の定義(使ってよいリソースの制約)

各サイズは「何を使ってよいか」で明確に線引きされています。Google公式の定義は次のとおりです。

  • Small:単一プロセスで実行する(多くの言語ではさらに単一スレッドに制限)。sleep・I/O操作・その他のブロッキング呼び出しは禁止で、ネットワークやディスクにもアクセスできません。データベースやファイルなど重い依存はテストダブルに置き換えます。制約が強い分、決定的(hermetic)で高速に走り、フレークが起きにくいのが利点です。
  • Medium:複数プロセス・スレッドを使え、ブロッキング呼び出しも許可されます。ネットワーク通信はlocalhostへの接続のみ可能で、それ以外のシステムには触れられず、テスト全体を単一マシン内に収めます。ローカルにDBやサーバを立てて行う結合テストが典型例です。
  • Large:Mediumのlocalhost制約を外し、テストとテスト対象システムが複数マシンにまたがることを許します。リモートクラスタ上のシステムに対して、本番に近い全体動作を確認するE2Eテストなどが該当します。
  • Enormous:Largeよりさらに大規模で時間のかかるテストのために用意された区分です。

一般に、サイズが大きいほど実行が遅く、より多くのインフラに依存するためフレークしやすくなります。だからこそ、小さく速く安定したテストを土台に厚く置くのが基本方針になります。

Bazelにおけるテストサイズ(タイムアウト・RAM・CPU)

この考え方を具体的な設定値に落とし込んでいるのがビルドツールBazelです。Bazelではテストの size 属性が、デフォルトのタイムアウトと想定リソース量を決めます。size を指定しない場合のデフォルトは medium です。

サイズ デフォルトのタイムアウト 想定RAM CPUコア
small short(1分) 20MB 1
medium moderate(5分) 100MB 1
large long(15分) 300MB 1
enormous eternal(60分) 800MB 1

タイムアウトはサイズとは独立に timeout 属性で上書きできます。また、テストをローカル実行するときは、Bazelがサイズをスケジューリングにも利用し、--local_ram_resources--local_cpu_resources の上限を尊重して重いテストを同時に走らせすぎないよう調整します。つまりサイズは「どれくらいの時間・メモリを見込むか」の宣言でもあるわけです。

テストサイズとテストピラミッド・スコープの関係

テストの配分を語るモデルとして有名なのがテストピラミッドです。ピラミッドは底部に多数のユニットテスト、中間に少数の結合テスト、頂点にごく少数のE2Eテストを置き、コストと実行時間のバランスを取ります。これは主として「スコープ」の配分を示すモデルであり、テストサイズとは別軸である点に注意してください。テストピラミッドそのものの層構成や、単体・結合・E2Eの配分を実装者目線で設計する手順は、テストピラミッドとは?単体・結合・E2Eの配分と実装者向けテスト戦略設計で詳しく解説しています。

実務では、スコープ(何を検証するか)でテストを設計しつつ、サイズ(実行に何を許すか)で速度と安定性を制御する、という二軸で捉えると設計がぶれません。ピラミッドの底=速くて安定した小さなテスト、という直感は、サイズの観点で裏付けられます。

CI/CDでのテストサイズの使い分け

テストサイズが最も効いてくるのがCI/CDです。実行頻度をサイズに応じて分けることで、素早いフィードバックと全体品質の保証を両立できます。

  • Small:全コミット・全プルリクエストで実行。数十秒で結果が出るため、開発者は変更直後に赤/緑を確認でき、バグを早期に潰せます。
  • Medium:マージ前チェックなどで実行し、モジュール間の結合やlocalhost経由の連携を検証します。
  • Large・Enormous:時間とインフラを消費するため、ナイトリービルドやリリース前のゲートに絞って実行します。フレークの影響を局所化する狙いもあります。

速いテストを高頻度で、遅いテストを低頻度で。この単純な原則をサイズという明示的なラベルで運用できることが、テストサイズという分類の実務的な価値です。

よくある質問

テストサイズと単体テスト・結合テストは同じもの?

別の軸です。単体/結合/E2Eは「検証するコードの広さ(スコープ)」を、テストサイズは「実行に許すリソース」を表します。相関はしますが一致はせず、狭いスコープでもブラウザが必要ならMedium、広いスコープでもテストダブルを使えばSmallになり得ます。

テストサイズはどうやって決める?

まず外部依存(ネットワーク・ディスク・別マシン)をどこまで使うかで決めます。何も使わず単一プロセスで完結するならSmall、localhost内で完結するならMedium、複数マシンに及ぶならLargeです。Bazelなどでは size 属性で明示し、指定しない場合はmediumが既定になります。

なぜSmallテストを増やすべき?

Smallは決定的で高速なため、全コミットで頻繁に回してもCIを詰まらせず、バグを早期に発見できるからです。逆にLargeは遅くフレークしやすいので本数を絞り、重要な全体動作の確認に用います。

テストサイズはGoogle以外でも使える?

使えます。BazelのsizeやJUnitのカテゴリ機能などで実装でき、CI/CDでの実行頻度や並列度の制御に広く応用できます。Googleのような大規模組織でなくても、テストの速度と安定性を管理する枠組みとして有効です。

関連記事

資料請求

RELATED POSTS 関連記事