Node.js Playwright E2E テスト
ブラウザーテストは別プロセスの HTTP サーバーへアクセスします。テストプロセスの DATABASE_URL を変更しても、サーバーが保持する DB プールの接続先は変わりません。Playwright のワーカーごとに pgmem サーバー、フォーク、アプリプロセスを持たせます。アプリの起動時にフォーク URL を渡し、各ケースの前に同じフォークをリセットします。アプリと DB 接続はワーカーの間ずっと維持されます。
ワーカー単位のアプリとケース単位のリセット
Section titled “ワーカー単位のアプリとケース単位のリセット”以下の fixture は、pgmem とアプリをワーカーごとに一度起動します。prepare はフォーク前にプロジェクトのマイグレーションと基準シードを適用します。自動 fixture の resetDb は、ブラウザーテストの前に毎回その基準状態へ戻します。appUrl はワーカー単位なので、同じワーカーの page テストは同じアプリプロセスにアクセスします。
import { test as base, expect } from '@playwright/test';import { PgmemServer, startTestApp } from '@pgmem/core';import { migrateAndSeed } from './prepare-db.js';
export const test = base.extend({ pgmem: [async ({}, use) => { const server = await PgmemServer.start({ database: 'app', prepare: ({ url }) => migrateAndSeed(url), }); try { const fork = await server.fork(); try { await use(fork); } finally { await fork.close(); } } finally { await server.close(); } }, { scope: 'worker' }],
appUrl: [async ({ pgmem }, use) => { const app = await startTestApp({ fork: pgmem, args: ['./src/server.js'], healthPath: '/health', }); try { await use(app.url); } finally { await app.close(); } }, { scope: 'worker' }],
resetDb: [async ({ pgmem, appUrl }, use) => { await pgmem.reset(); await use(); }, { auto: true }],});
export { expect };startTestApp は利用可能なループバックポートを探し、フォークの DATABASE_URL と PORT を子プロセスに渡し、GET /health を待ってからアプリを渡します。終了処理も行います。command、portEnv、databaseEnv、healthPath、起動時の timeoutMs は変更できます。src/server.js は PORT で待ち受け、プール作成時に DATABASE_URL を使い、準備完了後に GET /health に応答する必要があります。migrateAndSeed(url) は prepare から戻る前に、開いた DB 接続をすべて閉じてください。
import { test, expect } from './fixtures.js';
test('注文を作成する', async ({ page, appUrl }) => { await page.goto(`${appUrl}/orders`); await page.getByRole('button', { name: 'New order' }).click(); await expect(page.getByText('Order created')).toBeVisible();});
test('次のケースは準備済みデータから始まる', async ({ page, appUrl }) => { await page.goto(`${appUrl}/orders`); await expect(page.getByText('No orders')).toBeVisible();});別のデータセットを使う場合は、ワーカーのフォークにシードを入れて fork.snapshot() を呼び、fork.reset({ snapshot: seeded }) で戻します。ワーカー終了までスナップショットを保持してください。同一プロセス内の HTTP テストは API テスト を参照してください。
安全な並列実行
Section titled “安全な並列実行”Playwright の workers: 4 などでワーカー数を制限し、ワーカー単位の pgmem・fork・appUrl fixture を一緒に維持します。各ワーカーが異なるポートのアプリプロセスを起動し、自分のフォークだけを復元します。Playwright の並列モードで同じファイルのケースが別ワーカープロセスに分かれても、各ワーカーが独自の fixture を作れば分離されます。全ワーカーで外部アプリ URL や DB フォークを共有すると、この分離は失われます。同じワーカーでは次の reset 前に HTTP リクエスト、バックグラウンドジョブ、トランザクションを完了させます。pgmem とアプリのメモリ使用量に合わせてワーカー数を決めてください。