0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

dev サーバではなく、デプロイする静的 HTML に対して E2E を回す

0
Last updated at Posted at 2026-08-14

こんにちは。小学生向けのニュースサイト、こどもニュースをつくっています。

このサイトは Next.js の静的書き出し(output: 'export')で、ビルドすると web/out に HTML が並びます。それを Cloudflare Pages に上げているだけです。

E2E は npm run dev に対して回すのが手軽ですが、本命のスイートはビルド成果物に対して回しています。理由と、そのための設定の話です。

dev と本番で挙動が違う class がある

dev サーバに対する E2E は速くて便利です。ただ、dev でしか出ない挙動と、本番でしか出ない挙動があります。

  • dev には HMR のクライアントが入る。本番には無い
  • dev は都度レンダリングする。本番は事前生成された HTML を返す
  • 本番ビルドでは React の開発時警告が剥がれる
  • 静的書き出しでは、動的なルートが実際に生成されているかどうかがビルド時に決まる

最後が特に効きます。ページを増やしたときに generateStaticParams の実装が間違っていると、dev では見えるのに web/out にファイルが無い、ということが起きます。dev に対する E2E は、これを一切検出できません。

デプロイするものと同じ成果物を検証したいので、webServer にビルドと配信をまとめて書いています。

webServer: {
  command: `npx tsx scripts/build-web-cached.ts && npx serve web/out -l ${PORT} --no-clipboard --no-port-switching -L`,
  env: { KODOMO_NEWS_SITE_URL: BASE_URL },
  cwd: '../../',
}

serveweb/out を配信するだけです。Next.js のサーバは介在しません。本番の Cloudflare Pages も静的ファイルを返すだけなので、構成としてはほぼ同じになります。

環境変数でサイト URL を差し替える

静的サイトでは、canonical や sitemap に絶対 URL が焼き込まれます。本番の値のままビルドすると、テストで検証しづらいです。

// 本番 URL の既定 (web/.env.production の KODOMO_NEWS_SITE_URL) をテスト用に上書きし、
// spec の localhost:4501 前提(sitemap/robots/canonical の assert)を維持する。
env: { KODOMO_NEWS_SITE_URL: BASE_URL },

ビルド時に読む環境変数を、テスト用の値で上書きしています。これで sitemap の中身をそのまま検証できます。

ここは「テストのために本番と違うものをビルドしている」ことになるので、少し気持ち悪さがあります。ただ、URL 以外は同じなので、割り切りとしては許容範囲だと考えています。逆に本番 URL のままビルドすると、テストは URL を無視して検証することになり、そちらのほうが穴が大きいです。

ポートを固定しない

同時に複数の作業を進めているとき、ポートが衝突します。このプロジェクトでは git worktree を使うので、2 つの pre-commit が同時に走ることがあります。

// main=4501 / worktree=4502..4550(複数 worktree の pre-commit が衝突しない)
const PORT = WEB_E2E_PORT;

ポートは作業ディレクトリのパスから決定的に導いています。ランダムにすると、テストが失敗したときに「どのサーバを見ればいいか」が分からなくなります。決定的なら、同じ worktree では常に同じポートです。

ビルドが重いので、入力が同じなら省略する

この構成の弱点は、E2E のたびにビルドが走ることです。pre-commit で回すには重すぎます。

そこで、ビルドの入力(ソース・データ・ロックファイル・環境変数・Node のバージョン)のハッシュを取って、前回と同じなら省略するようにしました。

build-web-cached: 入力(web/ lib/ data/ 等)が前回ビルドから不変なら next build を省略。
KODOMO_NEWS_FORCE_BUILD=1 で無条件ビルド。

判定できないときは必ずビルドする側に倒しています。省略の判定が壊れたときに古い成果物を検証し続けるのが、いちばん困る失敗だからです。

並列数はコア数まで上げない

Playwright の worker 数は、既定でコア数の 50% にしています。100% にすると速くなりそうですが、実測では逆でした。

同じマシン(32 スレッド)でビルドをキャッシュ済みにして測ると、100% は 6 回中 2 回失敗、50% は 9 回中 0 回失敗です。所要時間はどちらも 12〜13 秒台で、差は誤差でした。

1 worker が Chromium 1 プロセスなので、コア 1 本には収まりません。worker をコア数に近づけると、描画や hydration の待ちが伸びて、アサーションのタイムアウトを踏みます。

速度が変わらないなら、安定する側を選ぶだけです。実験したいときのために、環境変数で上書きできるようにしてあります。

dev 用の設定も残す

とはいえ、開発中に毎回ビルドを待つのは無駄です。dev サーバに対して回す設定も別に用意しています。

  • 本命(pre-commit・npm test): ビルド成果物に対して
  • 日常の確認: 起動済みの dev サーバに対して

設定ファイルを分けて、共通部分だけを 1 か所にまとめています。速い確認と正しい確認は、どちらか一方に寄せずに両方持っておくのがよかったです。

まとめ

  • 静的書き出しのサイトでは、dev サーバへの E2E は「生成されなかったページ」を検出できない
  • 本命のスイートは build && serve out に対して回す。デプロイするものと同じ成果物を検証する
  • 絶対 URL を焼き込む生成物があるなら、ビルド時の環境変数をテスト用に上書きする
  • ポートは作業ディレクトリから決定的に導く。ランダムにしない
  • ビルドが重いなら入力ハッシュで省略する。判定できないときはビルドする側に倒す
  • worker 数はコア数まで上げない。速度が変わらないなら安定する側を選ぶ
  • 速い確認用の設定も別に残す
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?