test-environments について
test-environments は、開発・QA チーム向けのテスト環境設計スキルです。環境の階層化、コンテナ構成、外部依存のスタブ化、本番環境との整合性確認を扱います。
主な機能
- 01ローカル、CI、プレビュー、ステージングの設計
- 02Docker Compose によるテスト構成
- 03外部依存の種類に応じたスタブ選択
- 04MSW による HTTP 境界での API 模擬
- 05環境整合性チェックリスト
活用シーン
- 01ステージングと本番の差による見逃し防止
- 02PR ごとのプレビュー環境の分離
- 03CI から実際の外部 API を呼ぶ事態の回避
- 04再現可能なローカル・CI テスト環境の構築
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ petrkindlmann/ qa-skills --skill test-environmentsインストール後、Claude Code で/test-environmentsを入力して読み込まれたか確認します。
同梱ファイルreferences/docker-compose.mdreferences/stubbing.md
詳細
調査の質問
まず .agents/qa-project-context.md を確認してください。存在する場合はその内容を使用し、すでに回答済みの質問は省略してください。その後、次の質問に答えてください。
- 現在、環境はいくつありますか? ローカル開発、CI、ステージング、プレビュー、本番環境はありますか?必要な環境を設計する前に、現在の構成を把握してください。
- アプリはコンテナ化されていますか?
Dockerfile、docker-compose.yml、compose.yamlがあるか確認してください。ある場合は、マルチステージターゲットと Compose をすぐに利用できます。ない場合は、まずそれを用意します。 - テストデータはどのようにシードされていますか? 手動の SQL、マイグレーションベース、ファクトリーライブラリ、本番環境のスナップショットのどれですか?これによって、シードスクリプトをすぐに導入できるか、全面的な書き換えが必要かが決まります。
- ステージング環境は本番環境にどれくらい近いですか? 同じ DB エンジン、キュー、キャッシュ、認証プロバイダー、オーケストレーションを使っていますか?それぞれの不一致は、ステージングでは決して検出できないバグの原因になります。
- 外部依存関係: システムはサードパーティ API をいくつ呼び出していますか?非本番環境ではスタブ化されていますか?スタブ化されていないサードパーティは、CI が不安定になる最大の原因です。
基本原則
1. バグが潜む箇所では、ステージング環境を本番環境に合わせます。 ステージングで SQLite を使い、本番で PostgreSQL を使っているなら、ステージングのテストは本番の動作を何も証明しません。環境固有のバグが発生する箇所である、データベースエンジンとそのバージョン、キューシステム、キャッシュ層、認証プロバイダーを一致させてください。
2. 長期間使い続ける環境より、一時的な環境を優先します。 共有のステージング環境は、一つの壊れたデプロイがチーム全体を妨げるボトルネックになります。PR ごとのプレビュー環境なら分離して並行テストができます。ステージング環境は、リリース直前の最終検証にのみ使用してください。
3. 本番環境のコピーではなく、決定的なシードデータを使います。 本番環境のスナップショットには、PII、古くなった参照、再現不可能な状態が含まれます。一貫性があり、有効で、最小限のデータセットを生成するファクトリーからシードデータを作成してください。(ファクトリーパターンについては、test-data-management を参照してください。)
4. 外部依存関係は、内部深くではなく境界でスタブ化します。 サードパーティ API は信頼性が低く、レート制限があり、費用もかかります。MSW または WireMock を使って HTTP 境界でスタブ化してください。内部サービスクラスをモックすると、自分たちのコード間の統合バグが隠れてしまうため、決して行わないでください。
5. 環境設定をコードとして管理します。 URL、フラグ、認証情報、リソース制限など、環境間の違いはすべてバージョン管理し、レビューできるようにする必要があります。リポジトリから再現できない手動設定は禁止です。
環境戦略
環境の階層
| 環境 | 目的 | データ | 外部依存関係 | ライフサイクル |
|---|---|---|---|---|
| ローカル開発 | 高速な開発サイクル | シード済みのフィクスチャ、最小限 | スタブ化(MSW/WireMock) | 開発者が管理 |
| CI | 自動検証 | 実行ごとにシード、一時的 | スタブ化またはコンテナ化 | パイプラインごとに作成/破棄 |
| プレビュー | PR レビューと E2E | ファクトリーからシード | スタブ化またはサンドボックス | PR 作成時に作成、クローズ時に破棄 |
| ステージング | 本番前の検証 | 匿名化された本番環境に近いデータ | 実際の連携(サンドボックスアカウント) | 長期間稼働、定期的にリセット |
| 本番 | 実際のユーザー | 実データ | 実際の連携 | 常設 |
ローカル開発
高速なフィードバックを得られ、共有状態はありません。開発者がフルスタックをローカルで 2 分以内に起動できるようにします。
docker compose -f docker-compose.test.yml up -d
npm run db:seed
npm run dev
データベース、キャッシュ、キューなどのインフラ依存関係には Docker Compose を使用し、ホットリロードを高速にするため、アプリケーションはネイティブに実行します。外部 API は、開発モードで読み込む MSW ハンドラーを使ってスタブ化します。
CI 環境
完全にコンテナ化されており、パイプラインの実行ごとに新しく作成され、実行後に破棄されます。以下のブロックはジョブの**services: フラグメント**です。runs-on と steps と並べて jobs.<id>.services の下に配置してください。単独では有効なワークフローファイルではありません。
# .github/workflows/test.yml — fragment: nest under jobs.test.services
services:
postgres:
image: postgres:18-alpine
env:
POSTGRES_DB: testdb
POSTGRES_USER: test
POSTGRES_PASSWORD: test
ports: ['5432:5432']
options: >-
--health-cmd="pg_isready -U test"
--health-interval=5s
--health-timeout=3s
--health-retries=5
redis:
image: redis:8-alpine
ports: ['6379:6379']
options: >-
--health-cmd="redis-cli ping"
--health-interval=5s
--health-timeout=3s
--health-retries=5
Docker Compose と Testcontainers
テストに実際のインフラを提供する方法は 2 つあります。ライフサイクルをどこで管理するかに応じて選んでください。
- Docker Compose — テストスイートの前に起動する宣言型のスタック(
docker compose up --wait)で、通常はtrapで保護したスクリプトを使って後から停止します。ローカル開発、共有 CI スタック、多くのテストが同じサービス群を共有する E2E に最適です。 - Testcontainers(Node/JVM/Python/Go) — テストコードからコンテナを起動し、テストスイートまたはテストごとに自動で破棄します。Compose ファイルや保守が必要な
trapは不要です。分離されたプログラム可能なインフラ(テストクラスごとの使い捨て Postgres など)を必要とする統合テストに最適です。「テストが所有する一時的なインフラ」の 2026 年時点の標準であり、手作業で作成した Compose とtrapのスクリプトに代わる有力な選択肢です。
人と多数のテストでスタックを共有する場合は Compose を、各テスト(またはテストスイート)が個別の使い捨て環境を必要とする場合は Testcontainers を選んでください。
プレビュー環境(PR ごと)
プルリクエストごとに分離された環境が用意されます。レビュアーはリンクをクリックして、他の PR に影響を与えずに変更をそのままテストできます。
ホスティングの選択肢(2026 年時点)は、スタックに応じて選んでください。
- Vercel のプレビュー デプロイ — Next.js/静的サイト/サーバーレス向け。PR ごとの URL が自動で発行されます。
- Cloudflare Pages のプレビュー — Git と連携し、無料枠も充実しています。
- Render/Railway のプレビュー環境 — データベースを含むフルスタックに対応しています。
- Northflank、Qovery、Bunnyshell、Uffizzi — プレビューにフロントエンドだけでなくスタック全体が必要な場合に適した、一時環境向けのプラットフォーム(Kubernetes ベース)です。
各プレビューでは、環境のライフサイクルにデータベースブランチ(Neon、Supabase、PlanetScale 方式)を組み合わせてください。PR の作成時にブランチを作成し、クローズ時に削除します。こうすることで、共有のステージング DB ではなく、安価ですぐに使える分離された DB のコピーを各プレビューに用意できます。(test-data-management を参照してください。)
ローカル開発環境を CI と同等にするには、次を利用してください。
- Devcontainers(
.devcontainer/devcontainer.json)— VS Code、Codespaces、JetBrains に対応しています。「誰もが同じ Docker ベースの開発環境を使う」ための標準です。 - Tilt(
Tiltfile)— ホットリロードと複数サービスのオーケストレーションに対応した、Kubernetes を前提とするローカル開発ツールです。ステージング環境自体が K8s の場合に選んでください。
生成された URL に対して E2E を実行するフロントエンドのプレビューは、数行で設定できます。
- name: Run E2E against preview
env:
BASE_URL: ${{ steps.deploy.outputs.preview-url }}
run: npx playwright test --project=chromium
PR ごとの名前空間に紐づけ、クローズ時に自動で破棄するカスタム Docker プレビューは次のとおりです。
- name: Deploy preview
run: |
NAMESPACE="pr-${{ github.event.number }}"
docker compose -f docker-compose.preview.yml -p "$NAMESPACE" up -d
echo "preview-url=https://${NAMESPACE}.preview.example.com" >> "$GITHUB_OUTPUT"
- name: Teardown preview
if: github.event.action == 'closed'
run: |
NAMESPACE="pr-${{ github.event.number }}"
docker compose -p "$NAMESPACE" down -v
ステージング
本番インフラを再現する長期稼働環境です。環境の乖離を防ぐため、毎週または必要に応じてリセットしてください。
#!/bin/bash
# scripts/reset-staging.sh
set -euo pipefail
echo "Resetting staging database..."
psql "$STAGING_DATABASE_URL" -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
echo "Running migrations..." # migrations MUST recreate extensions + grants (see caveat below)
npm run db:migrate -- --env staging
echo "Seeding anonymized data..."
npm run db:seed -- --env staging --dataset production-anonymized
echo "Verifying staging health..."
curl -sf https://staging.example.com/health || exit 1
echo "Staging reset complete."
注意点: DROP SCHEMA public CASCADE は、スキーマのデフォルト権限やインストール済みの拡張機能(uuid-ossp、pgcrypto、…)も削除します。マイグレーションのパイプラインでこれらを再作成し(CREATE EXTENSION IF NOT EXISTS …、デフォルト権限の再付与)、そうしないとマイグレーションの手順が失敗します。何もない状態で CREATE SCHEMA public を実行すれば以前の権限が復元されるとは考えないでください。復元されません。
テスト用の Docker Compose
docker-compose.test.yml の例では、統合テストと E2E テスト用にスタック全体(アプリ、Postgres、Redis、1 回限りのシードコンテナ、Mailpit)を起動します。重要な点は次のとおりです。
- ヘルスチェックによって
depends_onの条件が満たされます。healthcheckとcondition: service_healthyがない場合、depends_onはコンテナの起動を待つだけで、サービスが接続を受け付ける状態になるまでは待ちません。その結果、テストとデータベースの起動が競合し、接続エラーで失敗します。 - シードは長時間稼働するサービスではなく、1 回限りのコンテナです。
depends_on: condition: service_completed_successfullyを使用するため、シード処理が終了コード 0 で終了した後にのみアプリが起動します。シードを長時間稼働するサービスとして構成すると、シード処理の途中でアプリが起動する競合が発生します。 - ホスト上で実行するテストにはポートの公開が必要です。 参照例ではホストポートをループバックにバインドし、ランナーの URL と一致させています。スタックを並列実行する場合は、プロジェクト名とホストポートをそれぞれ別にしてください。
- PostgreSQL 18 ではボリュームのレイアウトが異なります。 バージョンごとのデータディレクトリには
/var/lib/postgresqlをマウントしてください。その他のメジャーバージョンについては、イメージのドキュメントを参照してください。
完全な docker-compose.test.yml、元の終了ステータスを維持しながらベストエフォートでクリーンアップする統合テストランナー、(production ターゲットを含む)マルチステージ Dockerfile、MinIO の設定については、references/docker-compose.md を参照してください。
マルチステージ Dockerfile
1 つの base レイヤーで依存関係を一度だけインストールし、development、test、seed の各ステージでそれを再利用します。スリムな production ステージでは、本番用の依存関係(npm ci --omit=dev)のみを実行し、test ステージからビルド成果物をコピーします。この分割により、テスト用の依存関係とソースコードを配布イメージに含めず、各環境にそれぞれのエントリーポイントを用意できます。base では最新のフラグである npm ci --include=dev を使用してください。--production=false は旧式の --omit/--include 構文です。完全な Dockerfile は references/docker-compose.md を参照してください。
外部依存関係の管理
依存関係の種類ごとのスタブ戦略
| 依存関係の種類 | ローカル/CI の戦略 | ステージングの戦略 |
|---|---|---|
| 決済(Stripe) | モックレスポンスを返す MSW ハンドラー | sk_test_ キーを使った Stripe のテストモード |
| メール(SendGrid) | SMTP をキャプチャする Mailpit(Web UI は :8025、SMTP は :1025) | SendGrid のサンドボックスモード |
| 認証(Auth0) | テストキーを使うローカル JWT 発行者 | Auth0 の開発テナント |
| ストレージ(S3) | MinIO コンテナ(S3 互換) | ライフサイクルポリシーを設定した専用のテストバケット |
| 検索(Elasticsearch) | Testcontainers Elasticsearch | リセットスクリプトを備えた専用のテストインデックス |
| SMS(Twilio) | MSW ハンドラー | Twilio のテスト認証情報 |
避けてください:MailHog — メンテナンスが終了しており、最終リリースは 2020 年です。Mailpit(axllent/mailpit)を使用してください。同じポート(SMTP は 1025、UI は 8025)ですぐに置き換えられます。
HTTP スタブに MSW を使う
MSW 2.x を使って、HTTP の境界で外部 API をスタブ化します。msw の http と HttpResponse、msw/node の setupServer を使い、ライフサイクルを beforeAll/afterEach/afterAll に接続します。モックされていない外部呼び出しが実際のネットワークリクエストとして漏れるのではなく、テストで明確に失敗するように、onUnhandledRequest: "error" を設定してください。Stripe、SendGrid、ジオコーディングのハンドラーとサーバーのライフサイクルについては、references/stubbing.md を参照してください。
S3 の代替として MinIO を使う
ローカル/CI テストで実際の AWS にアクセスする代わりに、コンテナ内で S3 互換ストレージを実行します。AWS SDK の S3Client で endpoint、環境変数の認証情報、forcePathStyle: true(MinIO では必須)を指定して接続してください。Compose サービスとクライアントの設定については、references/docker-compose.md を参照してください。
スタブの検証としてのコントラクトテスト
スタブは実際の仕様からずれていくことがあります。すべてのスタブにコントラクトテストを組み合わせ、スタブが実際の API 仕様に適合していることを検証してください。詳しくは contract-testing を参照してください。
環境の同等性チェックリスト
非本番環境のセットアップ時または監査時に実行してください。
| 項目 | 質問 | 問題の兆候 |
|---|---|---|
| データベースエンジン | 本番環境と同じエンジン、バージョンですか? | テスト環境ではSQLite、本番環境ではPostgreSQL |
| データベーススキーマ | 同じマイグレーションパイプラインが適用されていますか? | ステージング環境での手動スキーマ変更 |
| データの構成 | シードデータはすべてのエンティティ状態をカバーしていますか? | 「正常系」のレコードしかなく、エッジケースがない |
| インフラストラクチャ | 同じコンテナーオーケストレーションを使用していますか? | CIではDocker Compose、本番環境ではKubernetes |
| ネットワーク | 内部サービスのトポロジーは同じですか? | テスト環境ではモノリス、本番環境ではマイクロサービス |
| 設定 | 環境変数が文書化され、バージョン管理されていますか? | 文書化されていない環境変数、手動セットアップ |
| 認証 | 同じ認証プロバイダー/フローですか? | テスト環境でハードコードされたトークンを使い、認証を回避 |
| 機能フラグ | 同じフラグ評価エンジンを使用していますか? | テスト環境ではフラグをハードコードし、本番環境ではLaunchDarklyを使用 |
| TLS/HTTPS | 証明書の処理方法は同じですか? | ステージング環境ではHTTP、本番環境ではHTTPS |
| タイムアウト/制限 | レート制限、プール、タイムアウトは同じですか? | テスト環境の無制限なタイムアウトがパフォーマンスの問題を隠す |
ファクトリーベースのシードデータパターンについては、test-data-managementを参照してください。
アンチパターン
共有ステージング環境を唯一のテスト環境にする。 ある開発者のデプロイ失敗が、全員の作業を止めてしまいます。 分離のためにPRごとの一時環境を使用し、ステージング環境はリリース前の最終検証にのみ使用してください。
テストデータに本番データベースのコピーを使う。 PIIのリスク、再現できない状態、テストを遅くする膨大なデータセットにつながります。 ファクトリーを使い、決定論的な値で最小限のシードデータを作成してください。
環境固有のコードパスを設ける。 if (process.env.NODE_ENV === "test") { skipAuth(); }
は、実際の認証フローをテストしていないことを意味します。環境条件分岐ではなく、依存性注入または設定によって実装を切り替えてください。
環境を手動でセットアップする。 セットアップに15ステップのWikiページが必要なら、1週間以内に内容が間違ってしまいます。
すべてをスクリプト化してください。必要な手順はdocker compose up -d && npm run db:seedだけにしてください。
外部サービスではなく内部サービスをスタブ化する。 システムが外部と通信するHTTP境界でスタブ化してください。内部モジュールをスタブ化すると、自社サービス間の統合バグが隠れてしまいます。
Docker Composeにヘルスチェックがない。 ヘルスチェックなしのdepends_onは、コンテナーの起動を待つだけで、サービスの準備完了までは待ちません。そのため、テストがデータベースと競合し、接続エラーで失敗します。
プレビュー環境を長期間稼働させる。 マージ後も残るプレビュー環境はリソースを浪費し、古い状態を蓄積します。PRのクローズ時に自動で破棄してください(if: github.event.action == 'closed')。
検証
生成した成果物に対して、最小のチェックから順に実行してください。
- Composeファイルが有効である —
docker compose -f docker-compose.test.yml config -qが終了コード0で終了します(イメージをプルする前にYAMLとスキーマのエラーを検出します)。 - スタックが正常に起動する —
docker compose -f docker-compose.test.yml up -d --wait --wait-timeout 60が終了コード0で終了します。失敗した場合は出力を確認してください。ヘルスチェックが通過する前に、イメージのプル、ビルド、シードコマンドで起動に失敗することもあります。 - データベースが接続を受け付ける —
docker compose -f docker-compose.test.yml exec postgres pg_isready -U test -d testdbがaccepting connectionsを報告します。また、ホスト側のテストランナーを実行し、公開ポートの接続URLを確認してください。 - Dockerfileが本番ターゲットをビルドできる —
docker build --target production -t app:prod .が成功し、docker run --rm app:prod npm ls --omit=dev --depth=0に開発用依存関係が表示されません。 - スタブの失敗が明示される —
onUnhandledRequest: "error"を指定してテストスイートを実行します。実際の外部への呼び出しがあれば、テストが黙って通るのではなくエラーになるはずです。
完了条件
- 環境インベントリが文書化されている(dev、CI、preview、staging、production)。各ティアの特性とアクセス方法に関する注意事項も記載されていること。
docker compose -f docker-compose.test.yml config -qとup -d --waitが終了コード 0 で完了すること。長時間実行サービスが設定済みの準備完了チェックを満たし、ワンショットのシード処理が終了コード 0 で完了すること。- マルチステージ Dockerfile で
--omit=devを指定してproductionターゲットをビルドできること(配布イメージに開発用依存関係が含まれていないこと)。 - シードスクリプトが冪等であること(2 回実行しても終了コード 0 で完了し、重複キーエラーが発生しないこと)。また、リポジトリにチェックインされていること。
- 外部依存関係が HTTP 境界で
onUnhandledRequest: "error"を指定してスタブ化されていること。本番以外の環境に実際のサードパーティ認証情報が含まれていないこと。 - 環境間のパリティの差異(例:CI では SQLite、本番では PostgreSQL)が文書化され、緩和策が講じられているか、課題として追跡されていること。
- PR のプレビュー環境が自動作成され、クローズ時に自動削除されること(
if: github.event.action == 'closed')。
参考ファイル(references/ 内)
- docker-compose.md —
docker-compose.test.ymlの全体(Postgres 18、Redis 8、ワンショットのシード処理、Mailpit)、trapで保護された統合テストランナー、マルチステージ Dockerfile(base/development/test/seed/production)、MinIO サービスと S3 クライアントの設定。 - stubbing.md — Stripe/SendGrid/ジオコーディング用の MSW 2.x ハンドラーと、
onUnhandledRequest: "error"を指定したsetupServerのライフサイクル。
関連スキル
- service-virtualization — 依存関係ごとにモック、スタブ、フェイク、実物のいずれを選ぶかを決めるためのフレームワーク、および WireMock/MSW の詳細。スタブ化の方針を 決める 場合はこちらを参照してください。このスキルでは、選択したスタブを環境に組み込みます。
- test-data-management — ファクトリーパターン、合成データ、データベースのシード処理、PR ごとの DB コピー向けの DB ブランチ作成(Neon/Supabase/PlanetScale)。
- ci-cd-integration — パイプライン設定、GitHub Actions のサービス、成果物の管理、シャーディング、セルフホストランナー。周辺のワークフローはこちらを参照してください。このスキルでは、そのワークフローが実行する対象のサービスを定義します。
- contract-testing — スタブが実際の API と一致することを検証する、コンシューマー主導のコントラクト。