並列テストで共有アカウントが壊れる
Playwrightのテストを並列で動かしていると、共有アカウントの設定変更だけが時々失敗することがあります。
テストAが設定を保存している途中で、テストBも同じアカウントを更新する。どちらも単体では通るのに、CIでまとめて動かすと落ちる。こういうやーつです。
全部を直列化するのはもったいない
簡単な対策は、Playwrightのworkersを1にすることです。
import { defineConfig } from '@playwright/test';
export default defineConfig({
workers: 1,
});
これなら共有状態の競合は避けやすくなります。でも、関係のないテストまで全部待たされます。
外部APIを使わない画面テストまで遅くなるので、私はこの設定を常用したくありません。
test lockで同じ資源を使うテストだけ止める
Playwright 1.63では、テストに名前付きのlockを指定できます。
同じlock名を持つテストは、ファイル・worker・projectをまたいで同時に実行されません。別のlockを持つテストは、そのまま並列に進みます。
import { test, expect } from '@playwright/test';
test('ユーザー設定を更新する',
{ lock: 'shared-user-settings' },
async ({ page }) => {
await page.goto('/settings');
await page.getByLabel('表示名').fill('テストユーザー');
await page.getByRole('button', { name: '保存' }).click();
await expect(page.getByText('保存しました')).toBeVisible();
},
);
test('別の設定を更新する',
{ lock: 'shared-user-settings' },
async ({ page }) => {
await page.goto('/settings');
await page.getByLabel('タイムゾーン').selectOption('Asia/Tokyo');
await page.getByRole('button', { name: '保存' }).click();
await expect(page.getByText('保存しました')).toBeVisible();
},
);
この2本は同じアカウント設定を使うので、同時には走りません。ほかのテストは別のlockを使うか、lock無しのまま並列実行できます。
lock名は画面名ではなく、競合する共有資源の名前にしてください。settings-pageよりshared-user-settingsの方が、何を守っているのか分かりやすいです。
グループ全体にlockを付ける
同じ資源を使うテストが多いなら、test.describe()にlockを付けられます。
test.describe('共有アカウントの設定', {
lock: 'shared-user-settings',
}, () => {
test('表示名を変更できる', async ({ page }) => {
await page.goto('/settings');
// 共有アカウントを使う処理
});
test('タイムゾーンを変更できる', async ({ page }) => {
await page.goto('/settings');
// 共有アカウントを使う処理
});
});
ただし、describeの中のテストがすべて同じ資源を変更する場合に使う方が安全です。読み取りだけのテストまで巻き込むと、必要以上に遅くなります。
複数の共有資源を同時に使う時
Playwright 1.63のtest lockは複数指定もできます。
test('請求情報を更新する', {
lock: ['shared-user-settings', 'billing-api'],
}, async ({ page }) => {
await page.goto('/billing');
// アカウント設定と請求APIの両方を変更する処理
});
複数のlockを取得してからテストが始まるので、片方だけ確保した状態で別のテストと競合する問題を避けやすくなります。
lockを付けても確認できないこと
test lockは、テスト同士の同時実行を整理する仕組みです。アプリのトランザクションや、外部サービス側のレート制限を保証するものではありません。
共有アカウントを使うテストでは、テスト終了後に元の状態へ戻す処理も必要です。lockを付けたから片付けを省略してよい、とはなりません。
また、利用中のPlaywrightが1.63未満ならこの書き方は使えません。まず実行環境のバージョンを確認してください(ここは環境によって違うので注意です)。
共有資源だけを止めるのがちょうどいい
並列実行は速いのですが、共有アカウントや外部サービスを同時に触ると競合します。だからといってworkersを1にする必要はありません。
競合する資源に名前を付け、その資源を使うテストだけにtest lockを付ける。これがPlaywright 1.63での現実的な落としどころです。
私なら、まずCIで失敗している共有資源を1つずつ洗い出します。lock名を付けたら、そのテスト以外の並列性は残しておきます。これで遅くしすぎずに不安定さを減らせますね。
詳しい制約や最新の書き方はPlaywright公式リリースノートと公式の並列実行ガイドで確認してください。

コメント