🦁

E2Eテスト前に環境検証を入れたら、失敗の切り分けが楽になった話

に公開

この記事は、ソフトウェアテストアドベントカレンダー2025の14日目の記事です。
https://qiita.com/advent-calendar/2025/softwaretesting

はじめに

こんにちは、QAエンジニアをしていますreoです。

CIでE2Eテストを回した結果、原因がアプリなのか環境なのか分からず、調査に時間を取られた経験はありませんか。特に、サービス構成が複雑なアプリケーションや、E2Eテストを並列で実行している環境では、些細な環境トラブルが数分〜数十分のタイムロスにつながることがあります。

そこで今回は、E2Eテストの前段でインフラの状態をチェックできるツール「goss」を導入し、環境起因の失敗を早い段階で検知することを試してみました。本記事では、その構成と、実際に使ってみて分かったメリット・限界について紹介します。

gossとは

gossは、サーバーの状態を手軽に検証できるインフラテストツールです。アプリケーションコードではなく、OS・ミドルウェア・ネットワークといった実行環境そのものをテスト対象にできる点が特徴です。
ファイルの存在、サービスの稼働状態、ポートの待受、HTTPレスポンスなどをYAML形式で宣言的に定義し、まとめて検証できます。

公式リポジトリ: https://github.com/goss-org/goss
ドキュメント: https://goss.readthedocs.io/en/stable/

例として、次のようなチェックを定義できます。

# goss.yamlの例
- 特定ポートが LISTENしているかの確認
- サービスが有効・起動しているかの確認
- HTTP エンドポイントにアクセスして200が返るかの確認

port:
  tcp:3306:
    listening: true

service:
  mysql:
    enabled: true
    running: true

http:
  http://localhost:8080:
    status: 200

次のコマンドで検証できます。

$ goss validate

実行結果例:

Port: tcp:3306: listening: matches expectation: [true]
Service: mysql: enabled: matches expectation: [true]
HTTP: http://localhost:8080: status: matches expectation: [200]

Total Duration: 0.523s
Count: 3, Failed: 0, Skipped: 0

YAMLで記述できるため学習コストが低く、公式ドキュメントでも "Goss in 45 seconds" と紹介されている通り、非常に高速に動作します。
類似ツールとしてServerspec がありますが、設定のシンプルさと実行速度の点で、E2Eテスト前に軽く環境を確認したい今回の用途にはgossが適していると感じました。

ちなみに、今回の検証にgossを導入した背景としては、以前チームメイトからgossというインフラテストツールを紹介されて一度触ってみたいと思っていたことがきっかけです。YAML形式で書けるため、普段バリバリにコードを書かない自分でも比較的始めやすいと感じました。

E2Eテスト前に環境チェックを組み込んでみた

今回の検証では、簡単なTodoアプリを対象に、CI上でgossによる環境チェックを組み込みました。

Todoアプリ自体はシンプルな構成ですが、検証環境としてはあえてフロント・バックエンド・DB・検索エンジンなど複数コンポーネントがある構成にしています。意図としては、どこか一つのコンポーネントでも起動していないとE2Eテストが失敗するという点で実際に稼働しているアプリケーションなどに近い動きをするCI環境を再現するためです。

  • トップページ
    トップページ

  • Todo一覧ページ
    Todo一覧ページ

  • フロントエンド: React + Vite (localhost:5173)

  • バックエンド: PHP (localhost:8080)

  • データベース: MySQL 8.0

  • 検索エンジン: Elasticsearch

  • キャッシュ: Redis

  • E2Eテスト: Playwright

詳細な構成やコードは、以下のリポジトリにまとめています。
https://github.com/reomin/testNewFeatures

GitHub Actionsでの構成

GitHub Actionsでは、E2Eテストの前にgossによる環境検証ステップを挟みました。検証に失敗した場合は、その時点でジョブを終了させE2Eテストは実行しないようにしています。
作成したワークフローは次の順番で進みます。

  1. 環境を起動
  2. gossをインストール
  3. gossによる環境検証を実行
  4. 検証が通った場合のみE2Eテストへ進む

この構成により、環境が整っていない状態でE2Eテストを走らせてしまうケースを防げるようになります。

goss.yamlの設定内容

goss.yamlには、E2Eテストを実行するための前提条件となる環境チェックをまとめています。
今回は検証用ということもあり、E2Eテストで利用する主要なコンポーネントについて、やや広めにチェック項目を設定しました。

具体的には、次のような点を確認しています。

  • Elasticsearchが起動しており、クラスタの状態が正常であること
  • アプリの状態確認用のエンドポイントにアクセスすると、ステータス200が返ってくるか
  • MySQLやRedisに実際に接続できるか
  • PHPコンテナからElasticsearchやRedisにアクセスできるか
http:
  http://localhost:9200/_cluster/health:
    status: 200
    timeout: 10000
    body:
      - '"status":"green"'
      - '"status":"yellow"'

  http://localhost:8080/health.php:
    status: 200
    timeout: 5000
    body:
      - '"status":"healthy"'

  http://localhost:8080/todosGet.php:
    status: 200
    timeout: 5000

  http://localhost:5173:
    status: 200
    timeout: 5000

command:
  mysql-connection:
    exec: "docker exec -i $(docker compose ps -q db) mysql -u user -ppassword -e 'SELECT 1'"
    exit-status: 0
    timeout: 10000

  redis-ping:
    exec: "docker exec -i $(docker compose ps -q redis) redis-cli ping"
    exit-status: 0
    timeout: 5000
    stdout:
      - "PONG"

  backend-redis:
    exec: 'docker exec -i $(docker compose ps -q app) php -r "try { $redis = new Redis(); $redis->connect(\"redis\", 6379); echo \"OK\"; } catch (Exception $e) { exit(1); }"'
    exit-status: 0
    timeout: 10000
    stdout:
      - "OK"

  backend-elasticsearch:
    exec: 'docker exec -i $(docker compose ps -q app) php -r "try { $ch = curl_init(\"http://elasticsearch:9200\"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $result = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($code == 200) echo \"OK\"; else exit(1); } catch (Exception $e) { exit(1); }"'
    exit-status: 0
    timeout: 10000
    stdout:
      - "OK"

これにより、単にコンテナが起動しているだけでなく、実際に利用可能な状態かを確認できます。

gossを組み込んだ効果と感じたメリット

CIの実行結果:

上の画像からも分かる通り、今回のケースでは、gossのインストールに約1秒、検証自体も約0.3秒で完了しました。E2Eテスト全体と比べると無視できる時間であり、前段に入れてもほとんど負荷になりません。
また、E2Eテストが失敗した際に「アプリの不具合なのか、環境の問題なのか」を切り分ける作業は意外と時間がかかります。gossを導入したことで、環境起因の失敗を事前に除外できるようになり、調査や再実行にかかる時間を減らせるようになりました。

gossでは検知できなかった問題

一方で、gossのチェックがすべて通っているにもかかわらず、E2Eテスト実行時にフロントエンドへ接続できないケースも発生しました。

調査したところ、フロントエンドのコンテナが起動直後にクラッシュし、自動再起動を繰り返していることが原因でした。gossを実行した瞬間だけはHTTP 200が返っていたため、環境検証自体は成功していましたが、E2Eテスト実行時にはすでにサービスが停止していました。

  • コンテナログを確認すると、npmのオプション依存関係が欠落しているため、Rollupモジュールが読み込めずクラッシュしていることが原因でした:
Error: Cannot find module @rollup/rollup-linux-x64-gnu.
npm has a bug related to optional dependencies

この経験から、短時間・一度きりの環境検証では、起動直後は応答するものの、その後不安定になるサービスを検知できないという欠点があることが分かりました。

まとめ

今回の検証では、E2Eテストの前に環境チェックを挟むことで、環境が原因で失敗するテストと、その後の無駄な調査時間を減らすことを目的としました。
gossを導入したことで、設定ミスや起動漏れといった問題を早い段階で検知でき、特にサービス数が多い環境では大きな効果を感じました。一方で、gossは一時点の状態しか確認できないため、サービスの不安定さまではカバーできないことに気づくことができました。

今回あげた問題に対して、gossは万能な解決策ではありませんでしたが、環境の複雑さに応じて導入することでE2Eテストの失敗と時間のロスを減らす有効な手段になりそうだと感じています。

Discussion