自動テストチュートリアル

Playwright、Seleniumなどの自動テストツールを学ぼう

セレクタの種類と使い方

セレクタの種類と使い方

セレクタは、HTML要素を特定するための記法です。E2Eテストでは、操作したい要素を正確に見つけるために使います。

セレクタとは?

セレクタは「どの要素を選択するか」を指定する文字列です。

// 'button' がセレクタ
await page.locator('button').click();

// '#submit' がセレクタ
await page.locator('#submit').click();

CSSセレクタ

CSSでスタイルを適用する際に使うセレクタです。最も一般的に使われます。

基本的なCSSセレクタ

セレクタ 説明 HTML
タグ名 タグで選択 button <button>
#id IDで選択 #submit <button id="submit">
.class クラスで選択 .btn <button class="btn">
* すべての要素 * すべて
// タグ名
await page.locator('button');

// ID
await page.locator('#submit-button');

// クラス
await page.locator('.primary-btn');

属性セレクタ

// 属性の存在
await page.locator('[disabled]'); // disabled属性を持つ要素

// 属性の値(完全一致)
await page.locator('[type="submit"]'); // type="submit"

// 属性の値(部分一致)
await page.locator('[class*="btn"]'); // classに"btn"を含む
await page.locator('[href^="/api"]'); // hrefが"/api"で始まる
await page.locator('[src$=".jpg"]'); // srcが".jpg"で終わる

組み合わせセレクタ

// 要素 + ID
await page.locator('button#submit'); // <button id="submit">

// 要素 + クラス
await page.locator('input.form-control'); // <input class="form-control">

// 複数クラス
await page.locator('.btn.btn-primary'); // <button class="btn btn-primary">

// 要素 + 属性
await page.locator('input[type="email"]'); // <input type="email">

階層セレクタ

// 子孫(スペース)
await page.locator('form input'); // form内のすべてのinput

// 直接の子(>)
await page.locator('ul > li'); // ulの直接の子のli

// 隣接する兄弟(+)
await page.locator('label + input'); // labelの直後のinput

// 一般的な兄弟(~)
await page.locator('h1 ~ p'); // h1の後のすべてのp

疑似クラス

// 最初の要素
await page.locator('li:first-child');

// 最後の要素
await page.locator('li:last-child');

// n番目の要素
await page.locator('li:nth-child(2)'); // 2番目のli

// テキストを含む
await page.locator('button:has-text("送信")');

// 無効化されている
await page.locator('input:disabled');
💡 セレクタの優先順位
  1. id - 最も確実(一意なので)
  2. data-testid - テスト専用の属性
  3. class - 変更されやすい
  4. タグ名 - 同じタグが多数ある場合は不確実

XPath

XMLやHTMLの要素を指定する記法です。複雑な条件に対応できます。

基本的なXPath

// タグ名
await page.locator('xpath=//button');

// ID
await page.locator('xpath=//*[@id="submit"]');

// クラス
await page.locator('xpath=//*[@class="btn"]');

// 属性
await page.locator('xpath=//input[@type="email"]');

// テキストで検索
await page.locator('xpath=//button[text()="送信"]');

// テキストを含む
await page.locator('xpath=//button[contains(text(), "送信")]');

階層のXPath

// 親から子へ
await page.locator('xpath=//form//input'); // formの下のすべてのinput
await page.locator('xpath=//form/div/input'); // form > div > input

// 親要素を取得
await page.locator('xpath=//input[@id="email"]/parent::div');

// 先祖要素を取得
await page.locator('xpath=//input[@id="email"]/ancestor::form');

// 兄弟要素
await page.locator('xpath=//label[@for="email"]/following-sibling::input');

XPathの例

<form id="login">
  <div class="form-group">
    <label>メール</label>
    <input type="email" name="email">
  </div>
  <button type="submit">ログイン</button>
</form>
// formの中の最初のinput
await page.locator('xpath=//form[@id="login"]//input[1]');

// "ログイン"というテキストを持つbutton
await page.locator('xpath=//button[text()="ログイン"]');

// type="email"のinputの親div
await page.locator('xpath=//input[@type="email"]/parent::div');
⚠️ XPathの注意点
XPathはHTML構造が変わると壊れやすいです。可能な限りCSSセレクタを使いましょう。

Playwrightの推奨セレクタ

Playwrightは、よりセマンティックなセレクタを推奨しています。

getByRole()

アクセシビリティのロールで要素を取得します(最も推奨)。

// ボタン
await page.getByRole('button', { name: '送信' });

// リンク
await page.getByRole('link', { name: 'ホーム' });

// テキストボックス
await page.getByRole('textbox', { name: 'メールアドレス' });

// チェックボックス
await page.getByRole('checkbox', { name: '利用規約に同意' });

主なロール:

ロール 対応する要素
button <button>, <input type="button">
link <a>
textbox <input type="text">, <textarea>
checkbox <input type="checkbox">
radio <input type="radio">
heading <h1> ~ <h6>

getByText()

テキスト内容で要素を取得します。

// 完全一致
await page.getByText('ログイン');

// 部分一致
await page.getByText('ログイン', { exact: false });

// 正規表現
await page.getByText(/ログイン|サインイン/);

getByLabel()

ラベルテキストで入力欄を取得します。

// <label>メールアドレス</label> <input>
await page.getByLabel('メールアドレス');

// <label for="email">メール</label> <input id="email">
await page.getByLabel('メール');

getByPlaceholder()

プレースホルダーで入力欄を取得します。

// <input placeholder="名前を入力">
await page.getByPlaceholder('名前を入力');

getByTestId()

テスト専用のdata-testid属性で取得します。

// <button data-testid="submit-btn">送信</button>
await page.getByTestId('submit-btn');

セレクタの選び方

// 良い例(推奨順)
await page.getByRole('button', { name: '送信' }); // 1. セマンティック
await page.getByTestId('submit-btn'); // 2. テスト専用ID
await page.locator('#submit-button'); // 3. ID
await page.locator('button[type="submit"]'); // 4. 属性

// 悪い例(避けるべき)
await page.locator('div > div > button'); // 構造依存
await page.locator('button:nth-child(3)'); // 順序依存
await page.locator('.css-xyz-123'); // 自動生成クラス
🎯 セレクタ選択のベストプラクティス
  1. getByRole(): 最優先(アクセシビリティも向上)
  2. getByTestId(): テスト専用IDを追加
  3. id属性: 一意で変更されにくい
  4. CSSセレクタ: シンプルで読みやすい
  5. XPath: 最後の手段(複雑な階層が必要な場合のみ)

実践例

ログインフォームのテスト:

test('ログインフォーム', async ({ page }) => {
  await page.goto('/login');

  // 推奨: getByLabel()
  await page.getByLabel('メールアドレス').fill('user@example.com');
  await page.getByLabel('パスワード').fill('password123');

  // 推奨: getByRole()
  await page.getByRole('button', { name: 'ログイン' }).click();

  // 代替: ID
  await page.locator('#email').fill('user@example.com');
  await page.locator('#password').fill('password123');
  await page.locator('#login-btn').click();

  // 代替: data-testid
  await page.getByTestId('email-input').fill('user@example.com');
  await page.getByTestId('password-input').fill('password123');
  await page.getByTestId('login-button').click();
});

まとめ

セレクタ種類 記法 推奨度
getByRole() getByRole('button') ★★★★★
getByTestId() getByTestId('id') ★★★★☆
ID #id ★★★★☆
属性 [type="submit"] ★★★☆☆
クラス .class ★★☆☆☆
XPath xpath=//button ★☆☆☆☆

次のチャプターでは、ブラウザイベントについて学びます。