Selenium/Appium Advent Calendar 2015 12日目です。
薄い話が面白くてしょうがないです。
2013年のAdvent CalendarでSelenium の locator とうまくつきあうための話を書きました。この話自体は2年経ったいまでも全然有効な話なのですが、追加でcss selectorについて掘り下げていきたいと思います。
Seleniumのlocatorの話
Seleniumのlocatorとしてはこれを使うべきという指針は、おおざっぱにいうとこんな感じでした。
- formではname属性を使う
- それ以外は基本css selectorを使う
css selectorに関してはこういうselctorがあるよという程度の紹介で、何を使うべきというのは語られていませんでした。
css selectorの中の優先度
具体的にどのようなselectorを使うかという話の前にもう少し抽象的な話をします。個人的な考え方として、以下の優先度で要素を特定できるようにするのがよいと思います
- ドキュメントのuniqueな識別子を利用するようなcss selector
- ドキュメントのセマンティクスを元に特定できるようなcss selector
- その他
はい何を言っているかって感じがしますね。
なお、本文書の主題としては、ドキュメントのuniqueな識別子をどう設計するかという話なので、セマンティクスに関してはふれません。どうせmicrodataとかをきちんと考えてセマンティクスに設計されたサイトなんて本当限られているわけですし。
uniqueな識別子とは
要はそのドキュメント(html)の中で一意に要素を特定するためのものです。典型的には、htmlのid属性というのはそのような用途に使うためのものです。
id属性はもともとそのような性質があるので、理解は速いですが、class属性だってそのように利用できます。要は、class属性に入れる値が、ドキュメントの中でuniqueになるようにすればいいのです。
<a href="/" class="link_to_top">top</a>
例えば上記のような感じです。上のclass属性はid属性であっても違和感がないとは思います。(じゃあclass属性じゃなくてidでいいじゃんって思うかもしれないですが、なんらかの事情でidには本当にuniqueでかつシステム的な値が入ることがありますし)
要はclass属性でもなんでもよいのですが、何らかのわかりやすいuniqueな識別子が事前にわかっていれば、テストからその要素をuniqueに扱えるということです。
uniqueな識別子を取り決める
事前にわかっているためには、テストを書く人と開発する人で事前にインターフェースとして取り決める必要があります。実はSeleniumのテストを書く上で重要なのはこういったコミュニケーションやプロセスをきちんと設計できるかどうかであったりします。
これまではこのようなuniqueな識別子利用するにはid属性やclass属性を使うことが一般的でした。しかし、id属性やclass属性はフロントエンドの実装においても重要な要素だったりするので、しばしば開発とテストでの競合が起きることもありました。(つまり、テストを書く人が安易に属性を追加しにくい状況が生まれてしまうのです)
カスタムdata属性を使う
そこで、最近お薦めているのがカスタムdata属性です。カスタムdata属性とはdata-*で始まるドキュメントに固有の独自データを格納することを想定した属性です。例えば、data-test-idみたいなカスタム属性に識別子をいれておけば、
<a data-test-id="link_to_top">top</a>
a[data-test-id='link_to_top']
や[data-test-id='link_to_top']
というようなcss selectorで取得できるようになります。
事前にどういう名前の属性を使うかを決めておきさえすれば、その属性を自由にテストを書く人が追加でき、それがアプリケーションに影響を与える可能性は極めて低くなります。
Capybaraで独自セレクタを登録する
uniqueな識別子として、カスタムdata属性をインターフェースとして取り決めたのですが、毎回上記のようなcss selectorを書くのはだるいです。link_to_top
のような指定だけで済むようにしたいです。
例えば、RubyでCapybaraを使ってSeleniumのテストを書いているのであれば、Capybaraで以下のように独自セレクタを実装し、find
で利用できるようになります。
Capybara.add_selector(:data_selector) do
css { |type| "[data-test-id='#{type}']" }
end
find(:data_selector, "link_to_top").click
なお、Capybara.default_selector
を設定しておけば、以下のようにもかけます。
Capybara.default_selector = :data_selector
find("link_to_top").click
まとめ
文中でも書きましたが、Seleniumのテストを書く際に重要なのは、テストを書く人と開発する人で協力できるような、体制プロセスを構築し、テストとテスト対象の間でのインターフェースをきちんと設計することです。
そのために、開発に影響がでにくいようなカスタムdata属性を使っていくのが、協力体制を維持継続していくためにも便利なのではと思っています。
それでは楽しいSeleniumライフを。
明日は@naoqoo2さんです。