壊れた理由を“画面と数字”で残す:Contract Testをアーティファクト化する実践(HTMLレポート/メトリクス運用)

はじめに

Contract Test(契約テスト)は、「入口の仕様(ルーティング、ヘッダ、リダイレクト、エラー時の振る舞いなど)を壊さない」ための強力な安全装置です。ところが、テストを回して 落ちた瞬間 によく起きるのがこの状況です。

  • CIが赤いのは分かるけど、どこが壊れたのか読み解くのが面倒
  • ログが長い/並列で流れて見づらい
  • “前回は通った” という事実しかなく、比較ができない
  • 失敗の再現に時間がかかる(手元では通う、など)

ここで効くのが テスト結果のアーティファクト化 です。
要するに、テストの「証拠」を 誰でも見られる形 に落として残す。これだけで、契約テストは“あると安心”から“運用で本当に役に立つ”へ変わります。

この記事では、入口のContract Testを例にしつつ、

  • Playwright:HTMLレポート/スクショ/トレース/JUnit
  • k6:サマリ出力(JSON/CSV)と閾値の結果
  • CI(例:GitHub Actions):成果物をArtifactsとして保存
  • メトリクス化:時系列で「劣化」「改善」を追えるようにする

を、最小構成から段階的に作ります。


座学

1) “アーティファクト化”のゴールは、原因究明のコストを下げること

テストを増やすだけだと、失敗時の確認コストも増えます。
アーティファクト化の目的はシンプルで、「誰が見ても、短時間で原因が分かる」状態にすることです。

入口のContract Testで特に残したい情報は次の4つです。

  1. 何が落ちたか(テスト名/失敗箇所)
  2. どう落ちたか(期待 vs 実際、HTTP status、ヘッダ差分、レスポンス)
  3. いつから落ちたか(前回との比較、初回失敗のコミット)
  4. 性能・安定性がどう変わったか(p95、失敗率、スループット)

これが揃うと、入口の変更(Nginx設定など)でも “調べる” より “直す” に時間が使えます。


2) HTMLレポートとメトリクスは役割が違う

HTMLレポートは、失敗時の「現場写真」です。

  • どのテストが落ちたか
  • エラーメッセージ、スタックトレース
  • スクリーンショット、ネットワークの痕跡(Playwrightならtrace)

**メトリクス(時系列)**は、「体調管理」です。

  • 今回のp95が悪いのは偶然?それとも継続劣化?
  • 失敗率がじわじわ上がっていないか
  • 設定変更で“改善した”ことを証明できるか

入口のContract Testは、仕様の破壊だけでなく、じわじわ劣化(タイムアウト増、p95悪化)も重大事故の前兆になります。だから「写真(HTML)」と「体調(メトリクス)」の両方が必要です。


3) 実装の勘所:残すべき成果物を最初から決める

おすすめの最小セットはこれです。

  • Playwright:HTML report + trace(失敗時) + screenshot(失敗時) + JUnit(CI向け)
  • k6:summary JSON + threshold result(失敗したら落とす) + 可能なら CSV(後で比較しやすい)

そしてCIでは、

  • 成果物を Artifactsとしてアップロード
  • できれば PRにリンク(見に行ける導線)
  • さらに余力があれば 時系列DBへ送る(Prometheus/Influx等)

この順番が現実的です。最初から大掛かりにすると運用が止まりやすいので、段階的に強化します。


ハンズオン

前提として、入口(Nginx)が http://localhost:8088 にあり、Contract Testを Playwright と k6 で実行している状況を想定します。ここでは「成果物を残す」ことに集中します。

1) Playwright:HTMLレポート/トレース/スクショを“失敗時だけ”残す

(1) playwright.config を成果物向けに設定

e2e/playwright/playwright.config.js の例です。

import { defineConfig } from "@playwright/test";export default defineConfig({
testDir: "./tests",
retries: 0,
use: {
baseURL: process.env.BASE_URL || "http://localhost:8088", // 失敗時に証拠を残す(常時ONだと重くなる)
screenshot: "only-on-failure",
trace: "retain-on-failure",
video: "retain-on-failure",
}, // HTMLレポート(CIで artifacts 化しやすい)
reporter: [
["html", { outputFolder: "playwright-report", open: "never" }],
// CIでテスト管理と相性が良い(任意)
["junit", { outputFile: "test-results/junit.xml" }],
["list"],
], // 失敗時のログを見やすくしたい時に有効(必要なら)
// workers: 1,
});

これで、失敗したときだけ

  • スクリーンショット
  • トレース(trace.zip)
  • 動画
    が残り、HTMLレポートに紐づきます。

(2) 実行

npx playwright test

成果物は主に次に出ます。

  • e2e/playwright/playwright-report/(HTMLレポート)
  • e2e/playwright/test-results/(trace、screenshot、video、JUnit等)

(3) ローカルでHTMLレポート確認

npx playwright show-report playwright-report

“どこで壊れたか”が一発で追えるようになります。入口周りはHTTPテスト中心でも、これがあると原因特定が劇的に速くなります。


2) k6:サマリをJSONで残して、比較できる形にする

k6は「実行結果がコンソールに出て終わり」だと、比較が難しいです。そこで サマリJSON を毎回保存します。

(1) サマリをファイルに出す(handleSummary)

e2e/k6/entry.jshandleSummary を追加します。

import http from "k6/http";
import { check, sleep } from "k6";export const options = {
vus: 20,
duration: "20s",
thresholds: {
http_req_failed: ["rate<0.01"],
http_req_duration: ["p(95)<400"],
},
};const BASE_URL = __ENV.BASE_URL || "http://host.docker.internal:8088";export default function () {
const res1 = http.get(`${BASE_URL}/`);
check(res1, {
"GET / is 200": (r) => r.status === 200,
}); const res2 = http.get(`${BASE_URL}/api/health`);
check(res2, {
"GET /api/health is 200": (r) => r.status === 200,
}); sleep(0.2);
}// ここがポイント:実行結果をJSONで吐く
export function handleSummary(data) {
return {
"k6-summary.json": JSON.stringify(data, null, 2),
};
}

(2) 実行してファイルができることを確認

docker run --rm -i grafana/k6 run - < e2e/k6/entry.js

実行ディレクトリに k6-summary.json が生成されます(CIではワークスペースに残る)。

このJSONがあるだけで、後から p95 や失敗率を抽出して比較できるようになります。


3) CIで“成果物として保存”する(例:GitHub Actions)

ここでは、CIが何であっても考え方は同じで、

  • テストを実行
  • レポートディレクトリやサマリJSONを生成
  • それらをArtifactsとしてアップロード
    という流れです。

(1) GitHub Actions の例(最低限)

.github/workflows/contract-test.yml の例です(概念を掴む用)。

name: contract-teston:
pull_request:
push:
branches: [ main ]jobs:
e2e:
runs-on: ubuntu-latest steps:
- uses: actions/checkout@v4 - name: Start stack (Nginx + API)
run: |
docker compose -f docker-compose.yml -f docker-compose.go.yml up -d --build - name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20" - name: Install Playwright deps
working-directory: e2e/playwright
run: |
npm ci || npm install
npx playwright install --with-deps - name: Run Playwright
working-directory: e2e/playwright
env:
BASE_URL: http://localhost:8088
run: npx playwright test - name: Run k6
run: |
docker run --rm -i -e BASE_URL=http://host.docker.internal:8088 grafana/k6 run - < e2e/k6/entry.js - name: Upload Playwright report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: e2e/playwright/playwright-report - name: Upload Playwright test-results (trace/screenshot/video/junit)
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-test-results
path: e2e/playwright/test-results - name: Upload k6 summary
if: always()
uses: actions/upload-artifact@v4
with:
name: k6-summary
path: k6-summary.json - name: Shutdown stack
if: always()
run: docker compose -f docker-compose.yml -f docker-compose.go.yml down -v

重要なのは if: always() です。
失敗したときほどレポートが欲しいので、落ちてもアップロードされるようにします。

これでPRから

  • Playwright HTMLレポート
  • traceやスクショ
  • k6のsummary JSON
    をダウンロードできるようになり、「壊れた理由の共有」が一気にラクになります。

4) メトリクス化の第一歩:k6 JSONから“必要な数値だけ”抜き出して保存

時系列DBに送る前に、まずは 比較可能な1枚のJSON を作るのが現実的です。例として、k6 summaryから次を抜くイメージです。

  • 失敗率(http_req_failed)
  • p95(http_req_duration p(95))
  • 平均(avg)
  • 直近コミットSHA(CIの環境変数で入れる)

CIの中で軽く整形して metrics.json を成果物にするだけでも、「前回より悪くなった」を議論しやすくなります。

(この段階ではDBは要りません。まず“比較しやすい形”にするのが勝ちです。)


5) 本格的に“時系列化”するなら:k6 → Prometheus/Influx → Grafana

余力が出たらここに進みます。代表ルートは2つです。

  • InfluxDBルート:k6の出力先として有名で、導入が比較的分かりやすい
  • Prometheusルート:周辺の観測(Nginx/アプリ/ノード)と統合しやすい

入口のContract Testを運用に乗せたいなら、最終的には「テスト結果」だけでなく、

  • Nginxのアクセスログの傾向
  • upstreamの応答時間
  • 502/504の増減
    などの“周辺情報”も同じダッシュボードに並べられると、診断速度がさらに上がります。

ただし、最初からそこまで行くより、

  1. Artifacts保存
  2. 指標抽出(JSON化)
  3. 時系列DB
    の順番が失敗しにくいです。

まとめ

Contract Testは、作るだけでは「赤い/緑」しか分かりません。
本当に効くのは、失敗時に 証拠が残る こと、そして性能劣化を 時系列で追える ことです。

  • Playwrightは HTMLレポート + trace + screenshot(失敗時だけ) が鉄板
  • k6は summary JSON を残すだけで比較が一気に楽になる
  • CIでは 落ちてもArtifactsを必ずアップロード(always)
  • メトリクス化は “まず抽出して保存” → “時系列DBへ” の段階設計が現実的

入口(Nginxやプロキシ設定)の変更はレビューが難しい領域だからこそ、アーティファクト化によって「説明できるテスト」にすると、運用の安心感が段違いになります。


投稿日

カテゴリー:

投稿者:

タグ:

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です