mcpField
W
webapp-testing-patterns

webapp-testing-patterns

github.com/bobmatnycテスト・品質78181 回インストール15 週間前に更新
リポジトリ

webapp-testing-patterns について

ブラウザー自動化テストを書く人向けの Playwright リファレンススキルです。セレクターからデバッグまでのパターンに加え、ネットワーク制御、画面収録、並列実行も扱います。

主な機能

  1. 01セレクター方式の選択
  2. 02ページや要素の状態待ち
  3. 03フォームやコントロールの操作
  4. 04内容と要素状態のアサーション
  5. 05ネットワーク応答のモックと監視
  6. 06スクリーンショットと動画の取得

活用シーン

  1. 01ログインやフォーム処理の自動化
  2. 02動的コンテンツの準備後にアサーション
  3. 03ブラウザーテストでの API 応答モック
  4. 04デバッグ用のテスト失敗記録

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill webapp-testing-patterns

インストール後、Claude Code で/webapp-testing-patternsを入力して読み込まれたか確認します。

同梱ファイルmetadata.json

詳細

Playwright パターンリファレンス

Playwright の自動化パターン、セレクター、ベストプラクティスの完全ガイドです。

目次

セレクター

テキストセレクター

テキストが一意の場合に、最も読みやすく保守しやすい方法です。

page.click('text=Login')
page.click('text="Sign Up"')  # Exact match
page.click('text=/log.*in/i')  # Regex, case-insensitive
ロールベースのセレクター

ARIA ロールに基づくセマンティックなセレクターです。

page.click('role=button[name="Submit"]')
page.fill('role=textbox[name="Email"]', '[email protected]')
page.click('role=link[name="Learn more"]')
page.check('role=checkbox[name="Accept terms"]')
CSS セレクター

対象を正確に指定するための従来の CSS セレクターです。

page.click('#submit-button')
page.fill('.email-input', '[email protected]')
page.click('button.primary')
page.click('nav > ul > li:first-child')
XPath セレクター

複雑な DOM ナビゲーションに使用します。

page.click('xpath=//button[contains(text(), "Submit")]')
page.click('xpath=//div[@class="modal"]//button[@type="submit"]')
データ属性

テスト専用セレクターのベストプラクティスです。

page.click('[data-testid="submit-btn"]')
page.fill('[data-test="email-input"]', '[email protected]')
セレクターの連結

セレクターを組み合わせて精度を高めます。

page.locator('div.modal').locator('button.submit').click()
page.locator('role=dialog').locator('text=Confirm').click()
セレクターのベストプラクティス

優先順位(最も安定したものから最も不安定なものの順):

  1. data-testid 属性(最も安定)
  2. role= セレクター(セマンティックでアクセシブル)
  3. text= セレクター(読みやすいものの、テキストが変わる可能性があります)
  4. id 属性(動的でなければ安定)
  5. CSS クラス(安定性が低く、スタイルの変更に伴って変わる可能性があります)
  6. XPath(壊れやすいため、可能なら避けてください)

待機戦略

読み込み状態の待機

動的なアプリケーションには不可欠です。

# Wait for network to be idle (most common)
page.goto('http://localhost:3000')
page.wait_for_load_state('networkidle')

# Wait for DOM to be ready
page.wait_for_load_state('domcontentloaded')

# Wait for full load including images
page.wait_for_load_state('load')
要素の待機

操作する前に、特定の要素が表示されるまで待機します。

# Wait for element to be visible
page.wait_for_selector('button.submit', state='visible')

# Wait for element to be hidden
page.wait_for_selector('.loading-spinner', state='hidden')

# Wait for element to exist in DOM (may not be visible)
page.wait_for_selector('.modal', state='attached')

# Wait for element to be removed from DOM
page.wait_for_selector('.error-message', state='detached')
タイムアウトによる待機

固定時間の遅延です(使用は控えめにしてください)。

# Wait for animations to complete
page.wait_for_timeout(500)

# Wait for delayed content (better to use wait_for_selector)
page.wait_for_timeout(2000)
カスタム待機条件

JavaScript の条件が満たされるまで待機します。

# Wait for custom JavaScript condition
page.wait_for_function('() => document.querySelector(".data").innerText !== "Loading..."')

# Wait for variable to be set
page.wait_for_function('() => window.appReady === true')
自動待機

Playwright は要素が操作可能になるまで自動的に待機します。

# These automatically wait for element to be:
# - Visible
# - Stable (not animating)
# - Enabled (not disabled)
# - Not obscured by other elements
page.click('button.submit')  # Auto-waits
page.fill('input.email', '[email protected]')  # Auto-waits

要素の操作

クリック
# Basic click
page.click('button.submit')

# Click with options
page.click('button.submit', button='right')  # Right-click
page.click('button.submit', click_count=2)  # Double-click
page.click('button.submit', modifiers=['Control'])  # Ctrl+click

# Force click (bypass actionability checks)
page.click('button.submit', force=True)
フォームへの入力
# Text inputs
page.fill('input[name="email"]', '[email protected]')
page.type('input[name="search"]', 'query', delay=100)  # Type with delay

# Clear then fill
page.fill('input[name="email"]', '')
page.fill('input[name="email"]', '[email protected]')

# Press keys
page.press('input[name="search"]', 'Enter')
page.press('input[name="text"]', 'Control+A')
ドロップダウンとセレクト
# Select by label
page.select_option('select[name="country"]', label='United States')

# Select by value
page.select_option('select[name="country"]', value='us')

# Select by index
page.select_option('select[name="country"]', index=2)

# Select multiple options
page.select_option('select[multiple]', ['option1', 'option2'])
チェックボックスとラジオボタン
# Check a checkbox
page.check('input[type="checkbox"]')

# Uncheck a checkbox
page.uncheck('input[type="checkbox"]')

# Check a radio button
page.check('input[value="option1"]')

# Toggle checkbox
if page.is_checked('input[type="checkbox"]'):
    page.uncheck('input[type="checkbox"]')
else:
    page.check('input[type="checkbox"]')
ファイルのアップロード
# Upload single file
page.set_input_files('input[type="file"]', '/path/to/file.pdf')

# Upload multiple files
page.set_input_files('input[type="file"]', ['/path/to/file1.pdf', '/path/to/file2.pdf'])

# Clear file input
page.set_input_files('input[type="file"]', [])
ホバーとフォーカス
# Hover over element
page.hover('button.tooltip-trigger')

# Focus element
page.focus('input[name="email"]')

# Blur element
page.evaluate('document.activeElement.blur()')

アサーション

要素の表示状態
from playwright.sync_api import expect

# Expect element to be visible
expect(page.locator('button.submit')).to_be_visible()

# Expect element to be hidden
expect(page.locator('.error-message')).to_be_hidden()
テキストの内容
# Expect exact text
expect(page.locator('.title')).to_have_text('Welcome')

# Expect partial text
expect(page.locator('.message')).to_contain_text('success')

# Expect text matching pattern
expect(page.locator('.code')).to_have_text(re.compile(r'\d{6}'))
要素の状態
# Expect element to be enabled/disabled
expect(page.locator('button.submit')).to_be_enabled()
expect(page.locator('button.submit')).to_be_disabled()

# Expect checkbox to be checked
expect(page.locator('input[type="checkbox"]')).to_be_checked()

# Expect element to be editable
expect(page.locator('input[name="email"]')).to_be_editable()
属性と値
# Expect attribute value
expect(page.locator('img')).to_have_attribute('src', '/logo.png')

# Expect CSS class
expect(page.locator('button')).to_have_class('btn-primary')

# Expect input value
expect(page.locator('input[name="email"]')).to_have_value('[email protected]')
件数とコレクション
# Expect specific count
expect(page.locator('li')).to_have_count(5)

# Get all elements and assert
items = page.locator('li').all()
assert len(items) == 5

テストの構成

基本的なテスト構造
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()

    # Test logic here
    page.goto('http://localhost:3000')
    page.wait_for_load_state('networkidle')

    browser.close()
Pytest の使用(推奨)
import pytest
from playwright.sync_api import sync_playwright

@pytest.fixture(scope="session")
def browser():
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        yield browser
        browser.close()

@pytest.fixture
def page(browser):
    page = browser.new_page()
    yield page
    page.close()

def test_login(page):
    page.goto('http://localhost:3000')
    page.fill('input[name="email"]', '[email protected]')
    page.fill('input[name="password"]', 'password123')
    page.click('button[type="submit"]')
    expect(page.locator('.welcome-message')).to_be_visible()
Describe ブロックによるテストのグループ化
class TestAuthentication:
    def test_successful_login(self, page):
        # Test successful login
        pass

    def test_failed_login(self, page):
        # Test failed login
        pass

    def test_logout(self, page):
        # Test logout
        pass
セットアップとティアダウン
@pytest.fixture(autouse=True)
def setup_and_teardown(page):
    # Setup - runs before each test
    page.goto('http://localhost:3000')
    page.wait_for_load_state('networkidle')

    yield  # Test runs here

    # Teardown - runs after each test
    page.evaluate('localStorage.clear()')

ネットワークのインターセプト

API レスポンスのモック
# Intercept and mock API response
def handle_route(route):
    route.fulfill(
        status=200,
        body='{"success": true, "data": "mocked"}',
        headers={'Content-Type': 'application/json'}
    )

page.route('**/api/data', handle_route)
page.goto('http://localhost:3000')
リソースのブロック
# Block images and stylesheets for faster tests
page.route('**/*.{png,jpg,jpeg,gif,svg,css}', lambda route: route.abort())
ネットワークレスポンスの待機
# Wait for specific API call
with page.expect_response('**/api/users') as response_info:
    page.click('button.load-users')
response = response_info.value
assert response.status == 200

スクリーンショットと動画

スクリーンショット
# Full page screenshot
page.screenshot(path='/tmp/screenshot.png', full_page=True)

# Element screenshot
page.locator('.modal').screenshot(path='/tmp/modal.png')

# Screenshot with custom dimensions
page.set_viewport_size({'width': 1920, 'height': 1080})
page.screenshot(path='/tmp/desktop.png')
動画の録画
browser = p.chromium.launch(headless=True)
context = browser.new_context(record_video_dir='/tmp/videos/')
page = context.new_page()

# Perform actions...

context.close()  # Video saved on close

デバッグ

実行の一時停止
page.pause()  # Opens Playwright Inspector
コンソールログ
def handle_console(msg):
    print(f"[{msg.type}] {msg.text}")

page.on("console", handle_console)
スローモーション
browser = p.chromium.launch(headless=False, slow_mo=1000)  # 1 second delay
詳細ログ
# Set DEBUG environment variable
# DEBUG=pw:api python test.py

並列実行

Pytest の並列実行
# Install pytest-xdist
pip install pytest-xdist

# Run tests in parallel
pytest -n auto  # Auto-detect CPU cores
pytest -n 4     # Run with 4 workers
ブラウザーコンテキストの分離
# Each test gets isolated context (cookies, localStorage, etc.)
@pytest.fixture
def context(browser):
    context = browser.new_context()
    yield context
    context.close()

@pytest.fixture
def page(context):
    return context.new_page()

コメント