4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

E2E自動テストを実装するためのロケーターの基礎

Last updated at Posted at 2023-05-19

はじめに

おしごとでWebのE2Eテスト自動化をやっているものの
正規表現然り、少し使ってないと書き方を忘れてしまうため、書き残しておきます。

ロケーターとは何かを知る上では次のスライドが大変わかりやすいです。
ロケーターを学んでテスト自動化上級者を目指そう1

ロケーターの選び方にも色々な考え方があり、どれが最善かは組織やテスト対象システムの作りによって変わってきます。
全てのテスト対象システムがはじめからE2E自動テストの実装に適した状態であるとは限らず、
リファクタリングを行わないと、自動テストの実装が困難な場合も出てくると思います。
一方で、機能追加や機能拡張の要望が強く、なかなかリファクタリングまで手が回らないという開発組織もあり、
やむを得なく複雑なロケーターを指定せざるを得ないという場面も多いのではないでしょうか。
E2Eテストの恩恵を理解してもらい、リファクタリングを進めてもらう努力をしつつ、
この記事を通じて、今ある状態でE2Eテストを実装するための技術を身につけるための一助になれば幸いです。

ロケーター一覧

CSSセレクターとXPathについて紹介していきます。

  • CSSセレクター
    元々はCSSでデザインを指定するときに、この要素はこういう色にしてね、こういう配置してね、と指定するときに使われるものです。

  • XPath
    Webページの情報を収集するクローラーなどでよく使われるものです。

要素名で指定する

要素名のみを指定して要素を特定するケースは少ないと思いますが、ルールを理解する上で重要なので念のため。

<input name="password">
  • CSSセレクター
    input
  • XPath
    //input
    なお、任意の要素名を選択したい場合は*を指定します。

id属性で指定する

<div id="main">
</div>
  • CSSセレクター
    #main
  • XPath
    //*[@id='main']

class属性で指定する

<button class="primaryButton">次へ</button>
  • CSSセレクター
    .primaryButton
    要素名の指定と組み合わせたい場合は、このように書きます。
    button.primaryButton
  • XPath
    //*[@class='primaryButton']
    こちらも同様に要素名と併せて指定すると、このようになります。
    //button[@class='primaryButton']

ただし、実際の実装だと、class属性に1つしか値が設定されていないことは少なく、
複数の値が設定されていることがほとんどです。
しかしながら、上記の書き方ではXPathで要素を選択することができません。
(CSSセレクターでは複数のclassが指定してあっても選択できます)

そこで次のように変更します。

<button class="primaryButton smallText nextButton--74XTSMX">次へ</button>
  • XPath
    • 複数のclassの中で対象のclassが含まれるかを示すcontains()を利用
      //button[contains(@class, 'primaryButton')]

また、ReactのEmotionというライブラリを用いて実装されている場合、
class名の末尾が変化する場合があり、classの一部に〜を含むという書き方をしたい場合があります。

<button class="primaryButton smallText nextButton--74XTSMX">次へ</button>
  • CSSセレクター
    • nextButton--を含む
      button[class*='nextButton--']
    • nextButton--で始まる
      button[class^='nextButton--']
    • Buttonで終わる
      button[class$='Button']
  • XPath
    • nextButton--を含む
      //button[contains(@class, 'nextButton--')]

id, class以外の属性で指定する

<div data-testid="password-area">
  <input name="password" value="パスワード">
</div>
  • CSSセレクター
    input[name='password']
    input[value='パスワード']
    規則性がわかってきたと思いますが、例えばdata-testid属性だったとしたら次のようになります。
    div[data-testid='password-area']

  • XPath
    //input[@name='password']
    //input[@value='パスワード']
    同様に、data-testid属性なら次のようになります。
    //div[@data-testid='password-area']

インナーテキストで指定する

<div>
  <span>重要</span>
  <span>あなたのアカウント名はhogehogeです</span>
</div>
  • CSSセレクター
    指定できない

  • XPath

    • 完全一致するもの
      //span[text()='重要']
    • 部分一致
      //span[contains(text(), 'アカウント名は')]
    • 〜で始まる
      //span[starts-with(text(), 'あなたのアカウント名は')]

時々、上記の指定では取得できない要素が出現します。デベロッパーツールでElementsを確認するとこんな感じになっている要素です。

<td>
    "山田"
    "太郎"
</td>

この場合、このような書き方をすると要素が取得できます。

  • XPath
    //td[.='山田太郎']

また、インナーテキスト内にタグが含まれるようなケースです。

<td>
    "氏名"
    <span>":"</span>
</td>

この場合、このような書き方をすると要素が取得できます。

  • XPath
    //td[.='氏名:']

階層構造を利用して指定する

<div>
  <span>
    <input> 
  </span>
</div>
  • CSSセレクター

    • 子を指定する
      div > span > input
    • 小孫を指定する(子要素を明示しない)
      div input
  • XPath

    • 子を指定する
      //div/span/input
    • 子孫を指定する(子要素を明示しない)
      //div//input

これを見てお気づきかと思いますが、おまじないのように先頭につけていた//は階層を省略していることを意味します。
ルートから省略せずに書くと次のようになりますが、E2Eテストを実装する際に指定するロケーターとして対象の要素を選択するには冗長なので、
ロケーターとして指定する際には先頭のパスは省略することがほとんどです。

<html>
  <body>
    <div>
      ...
    </div>
  </body>
</html>

/html/body/div/...

兄弟関係で指定する

<ul>
  <li>りんご</li>
  <li>いちご</li>
  <li>みかん</li>
</ul>
  • CSSセレクター
    ul > li:nth-of-type(2)
    <li>いちご</li>が選択される

  • XPath
    //ul/li[2]
    <li>いちご</li>が選択される

複数条件を指定する

XPathでは複数条件も指定することができます。
単独の条件では要素を一意に特定できない場合に利用します。

<div class="footer--7rfdx">お問合せはこちら</div>
  • XPath
    //div[contains(@class, 'footer' and text()='お問合せはこちら')]

andもあるなら、orもありますが、E2Eテストのロケーターとしてはあまり使わないかもしれません。

パスで指定する

XPath限定ですが、階層を戻ったり、たどったりすることができます。

<div>
    <h2>アカウント情報入力</h2>
    <div class="1">
        <label id="name">氏名</label>
    </div>
    <div>
        <input name="name">
    </div>
    <div class="2">
        <label id="address">住所</label>
    </div>
    <div>
        <input name="address">
    </div>
    <div class="3">
        <label id="phonenumber">電話番号</label>
    </div>
    <div>
        <input name="phonenumber">
    </div>
</div>
  • XPath
    • 親の要素を選択
      //label[@id='address']/..
      <div class="2">が選択される

    • 親の要素を選択
      //label[@id='address']/parent::div
      <div class="2">が選択される

    • 同じ階層にあり、かつ前に出てくる兄弟ノードの集合(preceding-sibling)を用いた選択の方法
      //label[@id='address']/../preceding-sibling::h2
      <h2>が選択される

    • 同じ階層にあり、かつ後に出てくる兄弟ノードの集合(following-sibling)を用いた選択の方法
      //label[@id='address']/parent::div/following-sibling::div[1]/input
      <input name="address">が選択される

今回はわかりやすいように選択される要素にclass属性やname属性を書いていますが、
実際には単独で指定しても要素を特定しにくいような場合に上記の記述方法が使えます。
XPathについてもっと詳しく知りたい場合は、参考文献2を見てみると良いでしょう。

おまけ

記述したロケーターが正しいことを確認するために、毎回最初からテストスクリプトを実行していると、膨大な時間がかかってしまいます。
手元のChromeでF12キーを押してデベロッパーツールを開き、Consoleタブを開いてロケーターを検証してみましょう。
image.png

CSSセレクターの検証

document.querySelector("{検証したいCSSセレクター}")

例)document.querySelector("input[name='password']")

XPathの検証

$x("{検証したいXPath}")

例) $x("//input[@name='password']")

参考文献

記事を書くにあたり、Selenium実践入門3の付録も大いに参考になりました。

  1. ロケーターを学んでテスト自動化上級者を目指そう. JaSST'23 Tokyo

  2. 便利なXPathまとめ. ZOZO TECH BLOG

  3. 伊藤 望, 戸田 広, 沖田 邦夫, 宮田 淳平, 長谷川 淳, 清水 直樹 & Vishal Banthia. Selenium実践入門 自動化による継続的なブラウザテスト. WEB+DB PRESS plus

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?