テストAug 26, 202617 分 READ

CircleCI Smarter Testing: 不要なテストの実行をやめる

Nathan Fish

プロダクト マネジメント担当ディレクター

テストは CI/CD パイプラインの実行時間の最大 50% を占めます。デリバリーパイプラインの中で最もコストのかかる部分であり、その時間のかなりの割合が、変更の影響を受けないテストの実行に費やされています。

開発者がコミットをプッシュするたびに、テストスイート全体が実行されます。auth.py の 2 行のバグ修正が 2,000 件のテストをトリガーし、そのうち 1,900 件は auth.py の周辺にまったく触れていません。こうした無関係なテストはほぼ確実にパスしますが、パイプラインはそれでも 20 分をかけて実行します。その間に開発者は別の作業へコンテキストスイッチしてしまい、結果が返ってきたときに元のタスクへ戻るのが難しくなります。

AI はこの状況をさらに悪化させています。開発者はこれまでにない速さでコードを生成し、テストを書けるようになりました。しかしコード作成が速くなれば、コミットは増え、テストは増え、すでに限界近くで動いていたパイプラインへの負荷はさらに高まります。テストスイートが 12 か月で 2 倍になってもパイプラインが賢くならなければ、開発者が待つ時間が 2 倍になるだけです。

Smarter Testing は、CircleCI がチームにその時間を取り戻してもらうための手段の 1 つです。結果への信頼性を損なうことなくテスト実行時間を短縮する 3 つの機能で構成されており (今後さらに追加予定)、CircleCI にネイティブに組み込まれているため、新しいベンダー、新しい契約、新しいインフラは一切必要ありません。先行ユーザーではテスト実行が最大 4 倍高速になっています。

この問題は見た目以上にコストが大きい

CI が遅いことのコストは、パイプラインの実行時間だけではありません。より大きなコストは、テストの実行中に失われる開発者の生産性です。

調査によると、中断された作業に完全に集中を取り戻すまでには平均 23 分かかります。30 分のテスト実行が奪うのは 30 分の生産性ではなく、作業セッション 1 回分です。これに 50 人のチームで 1 人あたり 1 日 4 コミットを掛け合わせると、エンジニアリングキャパシティのかなりの部分が待ち時間に消えていることになります。

CI が遅いと、優れた CI の習慣を維持することも難しくなります。小さく頻繁なコミット、継続的な検証、main への迅速な統合です。これらのプラクティスはリスクを下げ、レビューの質を高め、デリバリーを加速しますが、それを後押しするフィードバックループが閉じるのが遅すぎれば、チームはその習慣を身につけられません。

よくある対応策であるコンピューティングリソースの追加購入は、原因を解決せずに症状に対処するだけです。より大きなリソースクラスにアップグレードすれば、以降すべてのパイプライン実行のコストが上がり続けます。Smarter Testing は既存のコンピューティングリソースをより賢く使います。実行するテストは減り、ノードの負荷は均等になり、フレーキーテストが全体の再実行を引き起こすこともなくなります。

Clazar では、約 4,000 件のバックエンドテストがすべての PR に 12〜15 分を追加していました。Smarter Testing を有効にしたところ、フィーチャーブランチの実行は 3〜5 分に短縮されました。Liti では、PR のジョブが 4 倍高速になっています。

開発者は待っている間に別の作業へ切り替えていましたが、今は集中を保ったまま、 その場で問題を修正できます。テストへの信頼性を十分に維持したまま、 フィードバックループを大幅に短縮できました。CI/CD パイプラインに対して 行ってきた改善の中で、最も効果が高く、最も手間のかからないものの 1 つです。

— Ankit Raj、Clazar Member of Technical Staff

Smarter Testing の仕組み

Smarter Testing には、不要なテスト作業をなくすために設計された 3 つの機能が含まれています。テストパイプラインのどこで時間が無駄になっているかに応じて、個別に使うことも、組み合わせて使うこともできます。

1. Intelligent Test Selection: 必要なテストだけを実行する

最も大きな時間短縮は、変更の影響を受けようがないテストを実行しないことから生まれます。Test Impact Analysis (TIA) を基盤とする Intelligent Test Selection (ITS) は、まさにそれを行います。

このアプローチでは、コードカバレッジのデータを使って、テストがソースコードとどう関わっているかをマッピングします。安全性を保証し、選択もれを防ぐために、testsuite コマンドはまずテストスイート全体が選択された状態から始め、変更されたファイルの影響を受けないと証明されたテストだけを慎重に選択から外します。

これはサンプリングやヒューリスティックな推測ではありません。変更が auth.py に触れているなら、選択されるのは auth.py (およびそこから呼ばれるコード) をカバーするテストだけです。無関係なモジュールのテストはスキップされます。カバレッジのベースラインを維持するため main ブランチではスイート全体が実行されますが、素早いフィードバックが最も重要なフィーチャーブランチでは、テストの 60〜90% 以上を日常的にスキップできます。

ITS が適しているケース: ITS の効果が最も大きいのは、ほとんどの変更が局所的なコードベースです。大きなテストスイートがあり、多くの PR がコードベースの狭い範囲にしか触れないなら、ITS はフィーチャーブランチの実行ごとに大多数のテストをスキップします。開発者が高速に反復し、集中を保つために短いフィードバックループを必要とする AI 支援の開発ワークフローでは、とくに価値があります。

ITS を使うには、テストランナーがコードカバレッジのデータを出力できる必要があります。最近のフレームワークはほとんど対応しています (Jest、pytest、Go test、Vitest、RSpec、Mocha、Cypress など)。テスト自体を変更する必要はありません。カバレッジの出力を設定すれば、あとは TIA が処理します。

テストの信頼性について: TIA は推測ではなく実際のカバレッジデータに基づいているため、安心して任せられます。さらに Smarter Testing はどのテストがなぜ実行されたかを記録するので、テストスイートの可視性は下がるどころか、以前より高まります。Test Analytics ダッシュボードでは、スキップ率、実行時間の推移、テストごとの履歴を確認でき、任意のコミットで何が実行され何がスキップされたかを正確に把握できます。

2. Dynamic Test Splitting: アイドル時間をなくす

並列実行で複数のノードにテストを分散している場合、ノードの終了タイミングがそろわないことに気づいているはずです。あるノードが早々に終わっても、別のノードは長いテストファイルをまだ処理し続けています。ジョブ全体の時間は最も遅いノードで決まるため、そのアイドル時間は完全な無駄です。

静的なテスト分割は、過去の JUnit のタイミングデータをもとに、あらかじめテストをノードへ割り当てます。問題は、JUnit のタイミングが実際の経過時間を正確に反映していないことが多い点です。ランナーの起動、バッチごとのセットアップのオーバーヘッド、フックの実行といった固定コストが含まれていません。その結果、レポート上の所要時間は均等に見えても、実際の分割は偏ります。

Dynamic Test Splitting は別のアプローチを取ります。テストを事前に割り当てるのではなく、各ノードが継続的に取り出す共有キューを維持します。レポートされた時間と実際の実行時間のずれを時間をかけて学習し、固定コストやテストごとのオーバーヘッドを考慮します。起動が遅いノードや処理が遅いノードには小さいバッチが渡されるため、すべてのノードがほぼ同時に終了します。

Dynamic Test Splitting が適しているケース: 効果が最も大きいのは、テストの所要時間のばらつき、起動の遅延、タイミングデータの不正確さによって、並列ノードの終了時刻が明らかにずれている場合です。すでにノードが数秒差で終わっているなら、得られる効果は限られます。

3. Auto Rerun Failed Tests: フレーキーテストを自動で処理する

活発に開発されているテストスイートには、ほぼ例外なくフレーキーテストが存在します。タイミングの問題、外部サービスの呼び出し、環境の差異などによって断続的に失敗するテストです。従来の対応は、CI の失敗を待ち、どのテストが失敗したかを手作業で特定し、CircleCI の UI を開いて再実行をトリガーするというものでした。面倒で、時間がかかり、すべてが手作業です。

Auto Rerun Failed Tests は、この復旧を自動化します。テストが失敗すると、Smarter Testing は人の介入なしにただちに再試行します。この処理は、すべてのテストがパスするか、再試行の上限回数に達するまで繰り返されます。最終的にすべてのテストがパスすればエラーは抑制されてジョブは成功し、ノイズや手作業のトリアージなしにパイプラインは前に進み続けます。

再試行の挙動は、test-suites.yml の 2 つの設定で制御します。

  • max-auto-rerun: 失敗したテストアトムごとの再試行回数の上限 (0〜10)
  • auto-rerun-duration: 再試行に費やす時間の上限、または全体の実行時間に対する割合 (例: 5m20%)

両方を同時に設定でき、いずれかの条件に達した時点で再試行は停止します。

Auto Rerun Failed Tests が適しているケース: テストスイートに既知のフレーキーテストがあるなら、すぐに効果が出ます。とくに Intelligent Test Selection と併用すると有用です。実行するテストの総数が少ないときは、ごく一部のテストのわずかなフレーキーさでも誤った失敗につながります。Auto Rerun Failed Tests は、手作業なしにそれを拾い上げます。

なお、Auto Rerun Failed Tests が扱うのはフレーキーさであり、本物のバグではありません。実際のコードの問題でテストが失敗しているなら、再試行しても失敗し続け、最終的にジョブは失敗します。それが正しい挙動です。目的は、断続的な環境起因の失敗を、作業を止める問題として扱うのをやめることです。

無料で使える範囲と有料機能

circleci testsuite run コマンドに移行すると、従来の circleci tests run コマンドで提供されていたすべての機能が 無料 で使えます。テストの実行、並列ノードへの静的なテスト分割、失敗したテストのみの再実行です。testsuite に移行してこれらの機能だけを使う場合、追加コストは発生しません。

その土台の上に、Smarter Testing の有料機能 があります。Intelligent Test Selection (Test Impact Analysis を基盤とする)、Dynamic Test Splitting、Auto Rerun Failed Tests、そして Test Analytics ダッシュボードです。これらは請求期間ごとに保存されたテスト結果に基づいて課金され、どのプランにも十分な無料枠が含まれています。

Smarter Testing のセットアップ

3 つの機能はいずれも同じ土台の上に成り立っています。テストの検出方法と実行方法を定義する .circleci/test-suites.yml ファイルと、CI 上の既存のテストコマンドを置き換える circleci testsuite run コマンドです。これを一度設定すれば、あとは YAML ファイルにオプションを追加して個々の機能を有効にできます。

CircleCI Testing Wizard は、Smarter Testing の基本セットアップを代行する自動実行型の CLI ツールです。CLI のインストール、検証チェックの実行、修正が必要な箇所が出てきたときの Claude への引き継ぎまでを行います。

ステップ 1: doctor コマンドを実行する

最新の CircleCI CLI をインストール してください。次に、テストが置かれているディレクトリ (通常はリポジトリのルート) から doctor コマンドを実行します。

$ circleci testsuite doctor "ci tests"

CLI が一連のチェックを順に案内し、テストを実行して出力を検証し、設定に問題がありそうな箇所について具体的な対応項目を提示します。すべてのチェックがパスするまで手順に従ってください。

モノレポの場合: リポジトリのルートではなく、各サブパッケージのルートから CLI を実行してください。例: cd service-1 && circleci testsuite doctor "ci tests"。CLI は、実行された場所を基準に .circleci/test-suites.yml を探します。

doctor コマンドを CI のジョブステップとして実行し、実際の CI 環境で検証することもできます。

version: 2.1
jobs:
  doctor:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - run: circleci testsuite doctor "ci tests"

ステップ 2: テストコマンドを置き換える

doctor コマンドがパスしたら、.circleci/config.yml の既存のテストコマンドを circleci testsuite run "ci tests" に置き換えます。

変更前:

version: 2.1
jobs:
  test:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - run: npm test
      - store_test_results:
          path: test-reports

変更後:

version: 2.1
jobs:
  test:
    docker:
      - image: cimg/node:20.0
    steps:
      - checkout
      - run: circleci testsuite run "ci tests"

ステップ 3: 使いたい機能を有効にする

機能は .circleci/test-suites.ymloptions ブロックで有効にします。テスト結果を保存する最小構成は次のようになります。

---
name: ci tests
discover: jest --listTests
run: JEST_JUNIT_OUTPUT_FILE="<< outputs.junit >>" jest --runInBand --reporters=jest-junit --bail << test.atoms >>
options:
  store-test-results: true

あとは、使いたい機能のオプションを追加します。

Intelligent Test Selection (有料):

options:
  test-impact-analysis: true

Dynamic Test Splitting (有料、ジョブ設定で parallelism > 1 が必要):

options:
  dynamic-test-splitting: true

Auto Rerun Failed Tests (有料):

options:
  max-auto-rerun: 3
  # Optional: also limit by time
  auto-rerun-duration: 5m

3 つすべてを組み合わせることもできます。

---
name: ci tests
discover: jest --listTests
run: JEST_JUNIT_OUTPUT_FILE="<< outputs.junit >>" jest --runInBand --reporters=jest-junit --bail << test.atoms >>
options:
  test-impact-analysis: true
  dynamic-test-splitting: true
  max-auto-rerun: 3
  store-test-results: true

ステップ 4: ローカルと CI で確認する

ローカル

$ circleci testsuite run "ci tests"

CI 上

.circleci/test-suites.yml.circleci/config.yml の両方をフィーチャーブランチにコミットしてプッシュします。パイプラインの実行後に、次を確認してください。

  • Tests タブを開き、テスト件数が想定どおりかを確認します。大きく食い違う場合は、discover コマンドの設定に問題がある可能性があります。
  • ジョブの所要時間を、testsuite を使っていない直近の実行と比較します。ベースラインでは同程度になるはずです。Intelligent Test Selection による高速化は、影響マップが構築される以降の実行で現れます。
  • Dynamic Test Splitting については、Timings タブでテストがノード間により均等に分散されているかを確認します。

ROI の考え方

有料機能に投資すべきか判断するチームにとって、計算はシンプルです。20 分のテストスイートを持つ 100 人の開発チームで、各開発者が 1 日 4 回フィーチャーブランチにコミットする場合を考えてみます。

Smarter Testing がなければ、フィーチャーブランチの実行 400 回 × 20 分 = 1 日あたり 8,000 分のパイプライン時間です。Intelligent Test Selection がフィーチャーブランチのテスト量を 60% 削減すると、1 回の実行は平均 8 分程度、1 日あたり 3,200 分に下がります。1 か月では 105,600 分のコンピューティング時間の削減となり、さらに開発者はコミットごとに約 12 分を取り戻します。

開発者の総コストを 1 時間あたり 135 ドルとすると、その待ち時間の 25% を生産的な作業として取り戻せただけでも、毎月数万ドル分のエンジニアリングキャパシティの回復に相当します。保存されたテスト結果に基づく Smarter Testing の料金は、その金額に比べれば通常は誤差の範囲です。

ROI のほとんどは開発者の時間によってもたらされます。コンピューティングコストの削減も重要ですが、より大きな利点は、有用なフィードバックを開発者へ早く届け、変更をパイプラインの中で滞らせずに進められるようにすることです。

よくある問題のトラブルシューティング

Tests タブのテスト件数が想定と一致しない

多くの場合、test-suites.ymldiscover コマンドがすべてのテストファイルを見つけられていない、または対象外のファイルを拾っています。circleci testsuite doctor "ci tests" を再実行し、検出結果の食い違いを確認してください。glob パターンと、CLI が正しいディレクトリから実行されているかを確かめます。

Intelligent Test Selection を有効にした直後の実行で、まだすべてのテストが走る

これは想定どおりの動作です。ITS は、変更の影響を受けるテストを判断する前に、完全なカバレッジのベースラインを必要とします。main ブランチでの最初の実行がそのベースラインを作ります。以降のフィーチャーブランチの実行では、影響を受けるテストだけが選択されるようになります。

Dynamic Test Splitting でノードの負荷が均等にならない

Dynamic Test Splitting は、複数回の実行を通じてタイミングデータを蓄積するにつれて精度が上がります。最初のうちは静的な分割と大きく変わらないこともあります。何度実行してもノードの偏りが残る場合は、テストの実行時間に対してテストランナーの起動時間が長すぎないか確認してください。起動のほうがテスト本体より時間がかかる場合、複数のバッチに分割すると全体の実行時間がかえって増えることがあります。Dynamic Test Splitting は、テストの実行時間に比べてランナーの起動が速いときに最も効果を発揮します。

同じテストが再試行され続け、パスしない

一貫して失敗するテストはフレーキーではなく、壊れています。Auto Rerun Failed Tests は max-auto-rerun の上限まで再試行し、ジョブは正しく失敗します。そのテストが本当に断続的で、より多くの試行が必要なだけなら上限を上げてください。あるいは auto-rerun-duration を追加して、再試行に費やす時間を制限します。

circleci testsuite コマンドが見つからない

最新の CircleCI CLI がインストールされているか確認してください。circleci update を実行してアップグレードするか、cli.circleci.com から再インストールします。testsuite サブコマンドには、比較的新しいバージョンの CLI が必要です。

ローカルではテストがパスするのに CI では失敗する

circleci testsuite doctor "ci tests" を CI のジョブステップとして実行し、実際の CI 環境で設定を検証してください。作業ディレクトリ、利用できるツール、カバレッジの設定などの違いによって、testsuite コマンドの挙動はローカルと CI で変わることがあります。doctor コマンドはその差分を明らかにします。

Intelligent Test Selection でカバレッジデータが認識されない

TIA は、テストランナーがコードカバレッジを出力することを前提としています。テストコマンドに適切なカバレッジのフラグ (Jest なら --coverage、pytest なら --cov など) が含まれているか、test-suites.yml で出力パスが正しく設定されているかを確認してください。そのうえで doctor コマンドを再実行すると、カバレッジ出力の欠落や設定ミスを具体的に指摘してくれます。

モノレポでテストが正しく検出されない

リポジトリのルートではなく、サブパッケージのルートから CLI を実行しているか確認してください。testsuite コマンドは、実行された場所を基準に動作します。discoverrun のコマンド内で cd--directory フラグを使うのは避け、代わりにジョブステップ側で作業ディレクトリを指定してください。

使ってみる

不要なテストを減らせば、結果への信頼性を下げることなくフィードバックが速くなります。Smarter Testing は、テストパイプラインのどこで時間を失っているかに応じて、そこへ至るいくつかの道筋を用意しています。

まずは circleci testsuite run コマンド (無料、従来のコマンドと完全に同等) に移行し、テストの設定を確認してください。そのうえで、最大のボトルネックに効く有料機能を有効にします。多くのチームがすぐに最も大きな効果を得ているのは Intelligent Test Selection です。さらに Dynamic Test Splitting と Auto Rerun Failed Tests が無駄なテスト時間の別の要因を取り除くにつれ、効果は積み上がっていきます。

関連ドキュメント:

Frequently asked questions

What is CircleCI Smarter Testing?

Smarter Testing is a set of three CircleCI features that reduce test execution time without lowering confidence in your results: Intelligent Test Selection (run only the tests a change can actually affect), Dynamic Test Splitting (keep parallel nodes evenly loaded so none sit idle), and Auto Rerun Failed Tests (retry flaky failures automatically). All three are built natively into CircleCI, so there is no new vendor, contract, or infrastructure to manage. Early users are seeing up to 4x faster test runs.

Is it safe to skip tests with Intelligent Test Selection?

Intelligent Test Selection is driven by real code coverage data, not sampling or heuristic guessing. The testsuite command starts with your full suite selected and only deselects the tests proven to be unaffected by the files you changed. Your full suite still runs on your main branch to keep the coverage baseline current, and the Test Analytics dashboard records exactly which tests ran on any given commit and which were skipped.

Which test frameworks and languages does Smarter Testing support?

Smarter Testing works with any test runner that can produce code coverage data, which covers most modern frameworks, including Jest, pytest, Go test, Vitest, RSpec, Mocha, and Cypress. You do not need to change your tests. Configure coverage output, replace your test command with circleci testsuite run, and Test Impact Analysis handles the rest.

Is Smarter Testing free?

Adopting the circleci testsuite run command is free, and the free tier includes everything the legacy circleci tests run command offered: running tests, static test splitting across parallel nodes, and rerunning only failed tests. Intelligent Test Selection, Dynamic Test Splitting, Auto Rerun Failed Tests, and the Test Analytics dashboard are the paid additions. They are charged on stored test results per billing period, with free thresholds included in every plan tier.