モダンWebアプリのパフォーマンステスト:実践ガイド

Yunhao Jiao
モダンWebアプリのパフォーマンステスト:実践ガイド カバー

パフォーマンステストは、何か問題が起きるまでチームが最も一貫してスキップしてしまうテスト領域です。パターンはよく知られています:アプリケーションは開発・テスト環境では正しく動作し、本番環境にリリースされた後、実際のトラフィックが到来したり、大規模ユーザーが負荷の高い操作を実行したりした途端に落ちてしまいます。

本番環境でパフォーマンスの問題を発見するコストは高くつきます:ユーザーへの影響、インシデント対応時間、プレッシャーの中での緊急修正。それよりも早い段階——開発中——に発見するコストははるかに低くなります。本ガイドでは、本番環境がその重要性を教える前に、パフォーマンステストをワークフローに組み込む方法を解説します。

パフォーマンステストとは?

パフォーマンステストとは、アプリケーションが負荷下でどのように動作するかを評価する手法です。現実的な使用状況やピーク時の使用状況において、レスポンスタイム、スループット、リソース使用率、安定性を測定します。

パフォーマンステストには、いくつかの具体的な活動が含まれます:

負荷テスト — 想定される負荷下でアプリケーションはどのように動作するか?100人、500人、または1000人のユーザーが同時にアクティブな場合、レスポンスタイムは許容範囲内に収まるか?

ストレステスト — 通常のキャパシティを超えて負荷をかけた場合、アプリケーションはどのように動作するか?どこで限界を迎え、その際に適切に対処できるか?

スパイクテスト — 突発的なトラフィックの急増に対して、アプリケーションはどのように対応するか?スパイクが収束した後、適切に回復できるか?

ソークテスト — 長期間にわたる持続的な負荷下でも、アプリケーションは安定して動作し続けるか?メモリリーク、コネクションプールの枯渇、または時間の経過とともに表面化するその他の問題はないか?

ベースラインテスト — パフォーマンスのベースラインを確立し、将来の変更と比較できるようにします。これはCI/CD統合において最も価値あるパフォーマンステストの形式です。

パフォーマンスのリグレッションが検出しにくい理由

機能テストは正確性を検証します。パフォーマンステストは速度と安定性を検証します。ある機能が完全に正しく動作していても、壊滅的に遅い場合があります。機能テストスイートはパフォーマンスのリグレッションを検出できません。

機能テストをすり抜けるパフォーマンスリグレッションの一般的な原因:

  • AIが生成したORMコードによって引き起こされるN+1クエリのバグ。コード自体は正しく動作するものの、1回で済むデータベースクエリを数百回発行してしまう
  • すべての状態変更のたびに不必要な再レンダリングが発生するReactコンポーネント。動作は正しいが、UXの著しい低下を招く
  • 小規模データセット向けに最適化されたバックグラウンドジョブ。スケールアップに伴い処理速度が指数関数的に低下する
  • 複数のサービスからデータを集約するAPIエンドポイント。同時負荷時にレイテンシが増大する
  • 頻繁にクエリされるテーブルにインデックスなしのカラムを追加するデータベースマイグレーション

これらはいずれも機能テストの結果には現れない。しかし、実際のユーザーには多大な影響を与える。

注目すべきWebパフォーマンス指標

Webアプリケーションにおけるパフォーマンスは多面的です。ユーザー体験との相関が高い主要指標を以下に示します。

Core Web Vitals(GoogleのUX指標):

  • LCP(Largest Contentful Paint):メインコンテンツが表示されるまでの時間。目標値は2.5秒未満。
  • INP(Interaction to Next Paint):ユーザー操作に対するページの応答速度。目標値は200ms未満。
  • CLS(Cumulative Layout Shift):読み込み中にコンテンツが予期せずずれないか。目標値は0.1未満。

APIパフォーマンス:

  • p50レスポンスタイム:APIレスポンスのメジアンレイテンシ
  • p95/p99レスポンスタイム:リクエストの下位5%/1%(ユーザーが体感する遅延が集中する領域)のレイテンシ
  • 負荷時のエラーレート:トラフィック増加に伴いエラーレートが上昇するか
  • スループット:システムが性能劣化なく処理できる1秒あたりのリクエスト数

CI/CDへのパフォーマンステストの統合

パフォーマンスベースライン

CI/CDでパフォーマンスのリグレッションを検出する最も実用的な方法はベースライン比較です。現時点のアプリケーションパフォーマンスを計測してベースラインとして保存し、PRがベースラインから大きく逸脱した場合にアラートを発します。

すべてのPRでフルロードテストを実施する必要はありません。重要なエンドポイントと主要ページの読み込みを対象とした的を絞ったパフォーマンスチェックで、リグレッション検出には十分です。

パフォーマンステストのツール

k6 — モダンで開発者に使いやすいロードテストツール。JavaScriptベースのテストスクリプト、CLIによる実行、優れたCI/CD統合を備える。APIロードテストに最適。

Playwrightパフォーマンス — PlaywrightはE2Eテスト実行の一環としてCore Web VitalsとページパフォーマンスメトリクスをキャプチャできるE2Eテスト実行の一環。機能テストと並行してフロントエンドのパフォーマンスを追跡するのに有効。

Lighthouse CI — CI/CDでGoogle Lighthouseの監査を実行し、パフォーマンススコアが閾値を下回るとビルドを失敗させる。バックエンド負荷よりもフロントエンドパフォーマンス(Core Web Vitals)に最適。

TestSprite — TestSpriteのE2Eテスト実行は、機能テストの副産物として明らかなパフォーマンス問題を検出します。可視的なタイムアウトを引き起こすN+1クエリのバグ、低速なリストエンドポイントを生む欠落したページネーション、同期的なブロッキング処理はいずれもテスト失敗として機能テストレポートに現れます。専用のロードテストツールではありませんが、標準的なカバレッジの一環として、AIが生成するコードに最も多く見られるパフォーマンスバグを検出します。

実践的なCI/CDパフォーマンス設定

多くのチームにとって、現実的な出発点は以下のとおりです。

  1. ベースラインの確立:ステージング環境でLighthouse CIとk6スクリプトを実行し、結果を保存する。
  2. PRでのパフォーマンスチェックの実施:フロントエンドパフォーマンスのためにすべてのPRでLighthouse CIを実行。バックエンドコードに触れるPRでは重要なAPIエンドポイントにk6を使用。
  3. リグレッションのアラート:レスポンスタイムが20%以上増加した場合、またはCore Web Vitalsスコアが閾値を下回った場合にPRゲートを失敗させる。
  4. 主要リリース前のフルロードテスト:すべてのPRではなく、重要なリリースやインフラ変更の前にフルロードテストを実施する。

AIが生成するコードのパフォーマンステスト

AIコーディングツールは特有のパフォーマンスリスクをもたらすため、AIネイティブなチームにとってパフォーマンステストはより重要になります。

ORMクエリパターン。AIコーディングエージェントは正しく動作するものの非効率なORMクエリを生成することが多い。N+1クエリ、イーガーローディングの欠如、インデックスなしのルックアップなどが典型例。小規模データセットでは問題なく動作し、スケールアップ時に大きく劣化する。

Reactにおけるデータ変換。AIが生成するReactコンポーネントは、メモ化なしにレンダリングのたびに高コストな計算を実行することがある。機能的には正しく、開発環境では視覚的に検出できないが、実際のデータ量では顕著な遅延が生じる。

ページネーションの欠如。AIコーディングエージェントはページネーションではなく全レコードを返すリストエンドポイントを生成しがち。小規模データセットでは正しい動作だが、スケールアップ時には致命的な問題となる。

非同期コンテキストにおける同期処理。AIが生成するコードは、特にNode.jsのバックエンドにおいて、非同期処理が意図された箇所にブロッキング処理を持ち込むことがある。

CI/CDでのパフォーマンスベースラインによりこれらのリグレッションを複合する前に検出できます。TestSpriteのテスト実行は機能テスト結果とともにタイミングデータをキャプチャし、AIが生成するコードによるパフォーマンス問題の早期シグナルを提供します。

TestSpriteでパフォーマンスを意識したテストを始める →