コードを出荷する前に、カバレッジのギャップと影響を受けるテストを把握しましょう。テスト影響分析(TIA)をCIパイプラインに追加すると、これらの分析結果をまとめたプルリクエストのコメントが自動で投稿されます。ビルドが失敗したりマージがブロックされたりする心配はありません。パイプラインで影響を受けるテストを実行するように設定して、すぐにフィードバックを得ることもできます。
以下の手順に従って、TIAをCIワークフローに組み込みます。
TIAをローカルで試してみませんか?
AIコーディングエージェントを使用している場合は、mablテスト影響スキルを使うと、同じ分析をエディターから実行できます。スキルが変更内容を説明し、選択したテストを実行します。
前提条件
パイプラインにテスト影響分析を追加する前に、次のものを用意します。
- mabl APIキー。 「Command Line Interface」タイプのAPIキーをお勧めします。APIキーの管理を参照してください。
- 分析するアプリケーションのID。 mablリソースIDを参照してください。
-
パイプラインにインストールされたmabl CLI。 下のGitHub Actionsの例のように
npm install -g @mablhq/mabl-cliでグローバルにインストールするか、npm install --save-dev @mablhq/mabl-cliで開発依存関係としてインストールします。開発依存関係としてインストールした場合は、mablの代わりにnpx @mablhq/mabl-cliでコマンドを実行します。
CIパイプラインにテスト影響分析を追加する
mabl CLIを実行できるCIジョブであれば、mabl APIキーで認証してから、変更の説明を指定してmabl tests impactを実行します。
mabl auth activate-key "$MABL_API_KEY"
mabl tests impact \
--application-id {application-id} \
--change-description-file change.md \
--output markdown
コマンドは分析が完了するまで待機します。分析には数分かかります。
-
変更の説明の渡し方 - 複数行のテキストをシェルの引用符で囲まずに済むように、
--change-description-fileオプションを使用します。インラインで渡す場合は、代わりに--change-descriptionを使用します。 -
出力形式 - そのまま投稿できるレポートが必要な場合は
--output markdownを、影響を受けるテストを実行するステップなど、後続のステップで結果を読み取る場合は--output jsonを使用します。
すべてのオプションについては、mabl CLIコマンドリファレンスのmabl tests impactを参照してください。
変更の説明を書く
コード変更を読み取るmablテスト影響スキルとは異なり、CIでのTIAは差分を参照できません。TIAが把握できるのは変更の説明に書かれている内容だけで、通常はプルリクエストのタイトルと本文がそれに当たります。TIAは、そのテキストをステップの説明、アサーション、ページ名など、テストの内容と照合します。説明がテストでの製品の表現に近いほど、適切なテストを多く見つけられます。
CIでのTIAを最大限に活用するには、次のガイドラインに従ってください。
- プルリクエストから始めます。 人間のレビュー担当者に向けて書かれたタイトルと説明は、それだけで良い変更の説明の大部分を占めています。下のGitHub Actionsの例では、プルリクエストのタイトルと本文をそのまま使用しています。
-
実装の詳細ではなく、製品の言葉で変更を説明します。 mablのテストはファイルやクラスについて記述しないため、「
FieldGroup.tsxのバリデーションフックをメモ化した」のような説明では何も見つかりません。「バリデーションがフォーカスを外したときではなくキー入力ごとに実行されるようになり、ユーザーが入力している間にエラーテキストが表示される」のような説明のほうが適しています。 - 共有コードの場合は、変更が影響するユーザー向けの領域をすべて挙げます。 たとえば、あるバリデーターがチェックアウトと、顧客が保存済みの住所を編集する画面の両方で使われている場合は、説明にその両方を記載します。
コードのパスと製品の領域を対応付ける小さなマップをパイプラインに用意しておくと、差分だけでは得られない製品の言葉を補えます。
apps/web/src/checkout/** = checkout and payment
apps/web/src/account/** = account settings and saved payment methods
apps/web/src/search/** = product search and filtering
packages/forms/** = form validation
たとえば、packages/forms/**とapps/web/src/checkout/**に触れる変更では、説明に「Product areas touched: form validation, checkout and payment.」のような行が追加されます。
終了コード
分析結果が返ってくれば、影響を受けるテストが1つも見つからなかった場合も含めて、コマンドは0で終了します。0以外で終了するのは、呼び出し自体が失敗した場合だけです。たとえば、APIキーで認証できない、アプリケーションIDを解決できない、タイムアウトした、アカウントで生成AI機能が利用できない、といった場合です。コマンドが403で失敗する場合は、トラブルシューティングを参照してください。
終了コードは見つかったテストの数に左右されないため、このコマンドで件数に基づいてマージを制御することはできません。これは意図的な設計です。結果はレビュー担当者のための情報であり、合否の判定ではありません。
結果の例
--output markdownを指定すると、コマンドはそのまま投稿できるレポートを出力します。
## Test impact analysis (advisory)
Impacted: 2 · Coverage gaps: 1
**Impacted (2)**
- [Checkout postal code](https://app.mabl.com/workspaces/{workspace-id}/train/tests/{test-id}/current) - _validates_ · Asserts the inline error text while typing a postal code at checkout.
- [Account settings address edit](https://app.mabl.com/workspaces/{workspace-id}/train/tests/{test-id}/current) - Reaches the same validator when a saved address is edited.
**Coverage gaps (1)** — no credible coverage found for these areas; inconclusive, not proof they are untested
- No test covers the saved-payment expiry field.
**Summary** — the analysis agent's own account
> Searched checkout and account settings; the change reaches both.
Impacted: … · Coverage gaps: …の行の後には、さらに次の2行が表示されることがあります。
-
more_may_existがtrueの場合:「More relevant tests likely exist beyond the N returned; the list hit the result ceiling.」(返されたN件以外にも関連するテストが存在する可能性が高く、一覧が結果の上限に達しました)と、それに続く注記。 - 実行履歴を完全に解決できなかった場合:「Run history could not be fully resolved for some tests; treat missing run details as unknown.」(一部のテストの実行履歴を完全に解決できませんでした。欠けている実行の詳細は不明として扱ってください)
例: GitHub Actionsのワークフロー
このワークフローは、プルリクエストごとに分析を実行し、プルリクエストのタイトルと本文から変更の説明を作成して、結果をプルリクエストのコメントとして投稿します。ワークフローはGitHub CLI(gh)を使用します。GitHubホステッドランナーにはghがプリインストールされています。セルフホステッドランナーを使用する場合は、先にランナーにghをインストールしてください。
-
GitHubリポジトリで、mabl APIキーを
MABL_API_KEYという名前の暗号化されたシークレットとして保存し、アプリケーションIDをMABL_APPLICATION_IDという名前のリポジトリ変数として保存します。 -
.github/workflowsにtest-impact.ymlなどのワークフローファイルを追加し、次の内容を記述します。name: Test impact analysis on: pull_request: types: [opened, synchronize, reopened, edited] jobs: test-impact: runs-on: ubuntu-latest if: github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' permissions: contents: read pull-requests: write steps: - name: Analyze test impact continue-on-error: true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} MABL_API_KEY: ${{ secrets.MABL_API_KEY }} PR_URL: ${{ github.event.pull_request.html_url }} APPLICATION_ID: ${{ vars.MABL_APPLICATION_ID }} run: | npm install -g @mablhq/mabl-cli mabl auth activate-key "$MABL_API_KEY" { echo "Title: $(gh pr view "$PR_URL" --json title --jq .title)" echo gh pr view "$PR_URL" --json body --jq .body } > change.md mabl tests impact \ --application-id "$APPLICATION_ID" \ --change-description-file change.md \ --output json > impact.json jq -r ' "## Test impact analysis (advisory)\n\nImpacted: \(.tests | length) · Coverage gaps: \(.coverage_gaps | length)\n" + (if (.tests | length) > 0 then "\n**Impacted**\n" + ([.tests[] | "- " + .test_name + " — " + .context] | join("\n")) else "" end) + (if (.coverage_gaps | length) > 0 then "\n\n**Coverage gaps**\n" + ([.coverage_gaps[] | "- " + .] | join("\n")) else "" end) ' impact.json > impact.md gh pr comment "$PR_URL" --body-file impact.md -
プルリクエストを開くか、既存のプルリクエストにプッシュします。ジョブが完了すると、分析結果がプルリクエストのコメントとして表示されます。
ワークフローをそのまま追加した場合の動作は次のとおりです。いずれもパイプラインに合わせて変更できます。
-
プルリクエストが編集されると再実行されます。 GitHubは既定で、プルリクエストが開かれたとき、再度開かれたとき、新しいコミットが追加されたときにのみプルリクエストのワークフローを実行します。このワークフローはプルリクエストのタイトルと本文から変更の説明を作成するため、
editedも監視します。これにより、誰かが説明を更新すると分析が再実行され、コメントに最新の内容が反映されます。 -
分析を実行できなくても、ジョブは失敗しません。 分析ステップは、分析の結果によって失敗することはありません。失敗するのは、APIキーが機能しないなど、何らかの問題が発生した場合だけです。
continue-on-error: trueにより、そうしたエラーが発生してもジョブは成功のままになります。分析を実行できなかったときにジョブを失敗として表示したい場合は、この行を削除してください。 -
フォークとDependabotからのプルリクエストはスキップされます。 これらのプルリクエストはリポジトリのシークレットを読み取れないため、mabl APIキーを使用できません。ジョブの
if:条件により、これらのプルリクエストは失敗させずにスキップします。
例: チームに通知する
次のステップを、同じジョブの「Analyze test impact」の後に2つ目のステップとして追加します。このステップは分析を再実行せず、前のステップで保存したimpact.jsonを読み取ります。そのため、1回の分析結果をプルリクエストのコメントとチームへの通知の両方に使用できます。
- name: Notify the team
continue-on-error: true
env:
WEBHOOK_URL: ${{ secrets.TEAM_WEBHOOK_URL }}
run: |
summary=$(jq -r '
"Test impact: \(.tests | length) impacted tests, \(.coverage_gaps | length) coverage gaps"
+ ([.tests[0:3][] | "\n- " + .test_name] | add // "")' impact.json)
jq -n --arg text "$summary" '{text: $text}' \
| curl -sS "$WEBHOOK_URL" -H 'Content-Type: application/json' -d @-
SlackのIncoming WebhookとMicrosoft TeamsのIncoming Webhookは、どちらもtextフィールドを含むJSON本文を受け付けます。TeamsのワークフローWebhookは異なるペイロードを想定しているため、設定する前にWebhookが求める形式を確認してください。
影響を受けるテストを実行する
分析が返したテストを実行するには、それらのIDをmabl deployments createに渡します(最大50件)。mablはこれらのテストをデプロイメントイベントでまとめて1つのプラン実行として実行し、結果は他のデプロイと同様に テスト結果 > デプロイメント別 に表示されます。
--output jsonで分析を実行してから、各テストのtest_invariant_idを--test-idsに、分析のsession_idを--impact-session-idに渡します。
mabl deployments create \
--environment-id {environment-id} \
--application-id {application-id} \
--test-ids {test-id} {test-id} \
--impact-session-id {session-id} \
--await-completion
分析とは異なり、このコマンドは結果を報告します。--await-completionを指定すると、テストが完了するまで待機し、いずれかのテストが失敗した場合は0以外で終了します。--retry-failed-testsを指定した場合は、再実行で成功したテストは成功として扱われます。ステップでテストを開始するだけで完了を待たない場合は、--await-completionを省略してください。
すべてのオプションについては、mabl CLIコマンドリファレンスのmabl deployments createを参照してください。実行と分析の関係を変えるオプションには、次のものがあります。
-
--impact-session-idは、デプロイメントイベントを分析に関連付けます。これにより、分析がトリガーした内容を確認でき、GitHubチェックで選択内容を報告できます。--test-idsのテストはすべて、分析が返したものである必要があります。それ以外のテストを追加すると、mablはデプロイメントイベント全体を拒否します。 -
--mabl-branchは、mablブランチでテストを実行します。mabl tests impactに渡したものと同じブランチを使用してください。 -
--revisionは、GitHubインテグレーションを使用している場合に、結果をプルリクエストのGitHubチェックに関連付けます。 -
--test-credentialsは、個々のテストに、そのプランが使用するログインクレデンシャルを、スペース区切りの{test id}={credentials id}ペアとして渡します。--include-plansを指定して分析を実行し、各テストのrun_context.plan_credentials_idからペアを作成します。この値がないテストもあるため、それらはスキップします。jq -r '.tests[] | select(.run_context.plan_credentials_id) | "\(.test_invariant_id)=\(.run_context.plan_credentials_id)"' impact.json -
--retry-failed-testsは、失敗したテストを同じデプロイメントイベントで1回再実行します。--labelsに一致したプランには適用されず、--fast-failureと組み合わせることはできません。 -
--selection-nameは、結果に表示されるプラン実行の名前を指定します。既定値は「Selected tests」です。
--labelsを指定しない場合は、影響を受けるテストだけが実行されます。--labelsを追加すると、それらのラベルに一致するプランが同じデプロイメントイベントで実行されます。
例: GitHub Actionsのワークフローに実行ステップを追加する
上のGitHub Actionsのワークフローで、「Analyze test impact」の後に次のステップを追加します。このステップは前のステップで保存したimpact.jsonを読み取るため、分析が返したテストがそのまま実行されます。
- name: Run the impacted tests
env:
ENVIRONMENT_ID: ${{ vars.MABL_ENVIRONMENT_ID }}
APPLICATION_ID: ${{ vars.MABL_APPLICATION_ID }}
REVISION: ${{ github.event.pull_request.head.sha }}
run: |
if [ ! -s impact.json ] || [ "$(jq '.tests | length' impact.json)" -eq 0 ]; then
echo "No impacted tests to run."
exit 0
fi
SESSION_ID=$(jq -r '.session_id // empty' impact.json)
mabl deployments create \
--environment-id "$ENVIRONMENT_ID" \
--application-id "$APPLICATION_ID" \
--test-ids $(jq -r '.tests[].test_invariant_id' impact.json) \
${SESSION_ID:+--impact-session-id "$SESSION_ID"} \
--revision "$REVISION" \
--await-completion
分析結果にsession_idが含まれるとは限りません。${SESSION_ID:+...}の行は、session_idがある場合にのみ--impact-session-idを渡します。空のセッションIDやnullのセッションIDは、コマンドに拒否されるためです。
各テストで実行されるバージョン
各テストは、mablがデプロイメントイベントを受け付けた時点の、デプロイのmablブランチ上のバージョンで実行されます。そのブランチにバージョンがない場合は、masterのバージョンで実行されます。再試行や後から開始した再実行では、そのブランチの最新バージョンが使用されるため、その間に保存した修正も反映されます。
実行されないテスト
mablは無効になっているテストをスキップし、未実行として報告します。選択したテストがすべて無効になっている場合、mablはデプロイメントイベントを拒否します。また、次のような場合も、mablはデプロイメントイベント全体を拒否し、何も実行しません。
- 選択にブラウザーテストとAPIテストが混在している。
- モバイルテストなど、ブラウザーテストとAPIテスト以外の種類のテストが選択に含まれている。
- ブランチにもmasterにも、テストのバージョンがない。
- 環境がmablブランチのスナップショットからテストを実行していて、そのブランチにテストがない。この場合、masterにはフォールバックしません。
- イベントに一致するデプロイが、複数のmablブランチに解決される。
-
--impact-session-idで指定した分析が、いずれかのテストを返していない。 -
--test-credentialsのクレデンシャルIDが、--test-idsに含まれないテストを指定している、またはワークスペースにないクレデンシャルを指定している。
トラブルシューティング
分析が403で失敗する
アカウントで生成AI機能が利用できないか、APIキーに分析に必要なスコープがありません。別のワークスペースのキーや古いキーには、そのスコープがない場合があります。そうしたキーを使用したリクエストは、開始時ではなく途中で失敗し、再試行しても解決しません。新しいCommand Line Interface APIキーを作成して使用してください。