Playwrightの構文
Playwrightの基本構文 🧪
Section titled “Playwrightの基本構文 🧪”Playwrightのテストは、@playwright/testという組み込みのテストランナーを使用して記述します。これにより、テストの構造化とアサーションが簡単になります。
テストの作成
Section titled “テストの作成”テストは**test関数で定義し、検証にはexpect関数**を使用します。pageオブジェクトは、ブラウザ操作(ページの移動、クリック、入力など)を行うための中心的な役割を果たします。
const { test, expect } = require('@playwright/test');
test('基本テスト:ページのタイトルを検証', async ({ page }) => { await page.goto('https://example.com'); // ページのタイトルを検証します await expect(page).toHaveTitle(/Example Domain/);});expect(page).toHaveTitle()のように、アサーションは非常に読みやすく設計されています。
高度なテストシナリオ 🎯
Section titled “高度なテストシナリオ 🎯”Playwrightは、複雑なユーザーインタラクションを簡単に再現できます。
ユーザー操作の再現
Section titled “ユーザー操作の再現”page.click(), page.fill(), page.press()といったメソッドを使用して、クリック、テキスト入力、キーボード操作をシミュレートします。
test('複雑なユーザーインタラクション', async ({ page }) => { await page.goto('https://playwright.dev/login'); // フォームにユーザー名とパスワードを入力 await page.fill('#username', 'testuser'); await page.fill('#password', 'password123'); // ログインボタンをクリック await page.click('#login-button');
// ログイン後のウェルカムメッセージを検証 const welcomeMessage = await page.locator('#welcome-message'); await expect(welcomeMessage).toHaveText('Welcome, testuser!');});CI/CD統合 🤝
Section titled “CI/CD統合 🤝”Playwrightは、GitHub ActionsなどのCI/CDプラットフォームに簡単に統合できます。CI/CDパイプラインにテストを組み込むことで、コードが変更されるたびに自動でテストが実行され、デプロイ前にバグを発見できます。
GitHub Actionsの設定例
Section titled “GitHub Actionsの設定例”.github/workflows/playwright.ymlファイルを作成し、以下の内容を記述します。
name: Playwright Tests
on: [push, pull_request]
jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' - name: Install dependencies run: npm install - name: Install Playwright browsers run: npx playwright install --with-deps - name: Run Playwright tests run: npx playwright test - uses: actions/upload-artifact@v4 if: always() with: name: playwright-report path: playwright-report/ retention-days: 30このワークフローは、pushまたはpull_requestが発生するたびに自動でテストを実行し、テスト結果のレポートをアップロードします。
デバッグとトラブルシューティング 🐞
Section titled “デバッグとトラブルシューティング 🐞”Playwrightには、テストのデバッグを助ける強力なツールが組み込まれています。
Playwright UI
Section titled “Playwright UI”npx playwright test --uiコマンドを実行すると、GUIでテストをステップ実行したり、DOMスナップショットを確認したりできます。
コードの一時停止
Section titled “コードの一時停止”page.pause()をテストコードに挿入することで、特定の場所でテスト実行を一時停止し、ブラウザ上で要素の状態などを手動で確認できます。
test('デバッグテスト', async ({ page }) => { await page.goto('https://example.com'); await page.pause(); // ここでテストが一時停止 // 手動で操作を確認後、ターミナルでresume});パフォーマンス最適化 ⚡️
Section titled “パフォーマンス最適化 ⚡️”テストの実行時間を短縮し、効率を向上させるための手法です。
並列テスト実行
Section titled “並列テスト実行”Playwrightはデフォルトで並列実行をサポートしています。設定ファイルplaywright.config.jsでworkersの数を調整できます。
module.exports = { // 並列実行するworkerの数を指定 workers: process.env.CI ? 2 : undefined,};CI環境ではworkersの数を明示的に指定することで、CIサーバーのリソースを最大限に活用できます。
テストの分離
Section titled “テストの分離”各テストケースは独立して実行されるように設計します。これにより、並列実行の効果が最大化されます。
セキュリティテスト 🔐
Section titled “セキュリティテスト 🔐”Playwrightは、Webアプリケーションのセキュリティテストにも利用できます。
ユーザー認証のテスト
Section titled “ユーザー認証のテスト”認証APIが正しく動作するか、不正な認証情報が拒否されるかをテストします。
test('不正なログイン', async ({ page }) => { await page.goto('https://playwright.dev/login'); await page.getByLabel('Username').fill('invalid-user'); await page.getByLabel('Password').fill('wrong-password'); await page.getByRole('button', { name: 'Log in' }).click();
// エラーメッセージが表示されることを検証 const errorMessage = page.getByText('Invalid credentials'); await expect(errorMessage).toBeVisible();});APIリクエストの検証
Section titled “APIリクエストの検証”page.route()メソッドを使って、特定のリクエストをインターセプトし、レスポンスを検証することも可能です。これにより、アプリケーションが機密データを正しく扱っているかをテストできます。
test('APIリクエストの検証', async ({ page }) => { let interceptedRequest = null;
await page.route('**/api/login', route => { interceptedRequest = route.request(); route.continue(); });
await page.goto('https://example.com/login'); await page.getByLabel('Username').fill('testuser'); await page.getByLabel('Password').fill('password123'); await page.getByRole('button', { name: 'Log in' }).click();
// リクエストが正しく送信されたか検証 expect(interceptedRequest.postData()).toContain('testuser');});7. テストの構造化
Section titled “7. テストの構造化”test.describe()でテストをグループ化
Section titled “test.describe()でテストをグループ化”import { test, expect } from '@playwright/test';
test.describe('ログイン機能', () => { test('正常なログイン', async ({ page }) => { await page.goto('https://example.com/login'); await page.getByLabel('Username').fill('validuser'); await page.getByLabel('Password').fill('validpassword'); await page.getByRole('button', { name: 'Log in' }).click();
await expect(page).toHaveURL(/dashboard/); });
test('不正な認証情報でのログイン', async ({ page }) => { await page.goto('https://example.com/login'); await page.getByLabel('Username').fill('invaliduser'); await page.getByLabel('Password').fill('wrongpassword'); await page.getByRole('button', { name: 'Log in' }).click();
await expect(page.getByText('Invalid credentials')).toBeVisible(); });});test.beforeEach()とtest.afterEach()
Section titled “test.beforeEach()とtest.afterEach()”test.describe('ユーザー管理', () => { test.beforeEach(async ({ page }) => { // 各テストの前に実行 await page.goto('https://example.com'); await page.getByRole('button', { name: 'Log in' }).click(); await page.getByLabel('Username').fill('admin'); await page.getByLabel('Password').fill('admin123'); await page.getByRole('button', { name: 'Log in' }).click(); });
test.afterEach(async ({ page }) => { // 各テストの後に実行 await page.getByRole('button', { name: 'Log out' }).click(); });
test('ユーザー一覧の表示', async ({ page }) => { await page.getByRole('link', { name: 'Users' }).click(); await expect(page.getByText('User List')).toBeVisible(); });});8. 実務でのテストパターン
Section titled “8. 実務でのテストパターン”パターン1: フォーム送信のテスト
Section titled “パターン1: フォーム送信のテスト”test('フォーム送信のテスト', async ({ page }) => { await page.goto('https://example.com/contact');
// フォームに入力 await page.getByLabel('Name').fill('John Doe'); await page.getByLabel('Email').fill('john@example.com'); await page.getByLabel('Message').fill('Hello, this is a test message.');
// 送信 await page.getByRole('button', { name: 'Submit' }).click();
// 成功メッセージの確認 await expect(page.getByText('Thank you for your message')).toBeVisible();});パターン2: 検索機能のテスト
Section titled “パターン2: 検索機能のテスト”test('検索機能のテスト', async ({ page }) => { await page.goto('https://example.com');
// 検索ボックスに入力 await page.getByPlaceholder('Search...').fill('Playwright'); await page.getByRole('button', { name: 'Search' }).click();
// 検索結果の確認 await expect(page.getByText('Search results')).toBeVisible(); await expect(page.getByText('Playwright')).toBeVisible();});パターン3: モーダルダイアログのテスト
Section titled “パターン3: モーダルダイアログのテスト”test('モーダルダイアログのテスト', async ({ page }) => { await page.goto('https://example.com');
// モーダルを開く await page.getByRole('button', { name: 'Open modal' }).click();
// モーダルが表示されることを確認 const modal = page.getByRole('dialog'); await expect(modal).toBeVisible();
// モーダル内の操作 await modal.getByLabel('Input').fill('Test value'); await modal.getByRole('button', { name: 'Save' }).click();
// モーダルが閉じることを確認 await expect(modal).toBeHidden();});