最近、Web UIのテストでカスタムデータ属性でDOM要素を指定するやり方が気に入ってます。それについてまとめてみようと思います。
この手法はWebのテストのみならず、JavaScriptのスクリプトでも適用できるのですが、今はPhoenix LiveViewのテストの勉強中なのでWeb UIテストの文脈で書いてます。
TL;DR
今書いている時点で、一般的によく使われるID セレクターより、個人的にデータ属性を使用してDOM要素を取得するのが好みです。特にdata-role
というカスタム属性を使うことにより、アプリ全体で一貫した規約的なものができ、かつ柔軟にその要素の役割を明示することにもなると考えています。
仮にこういうHTML文書があったとします。
defmodule MnishiguchiWeb.AlchemistsLive do
use MnishiguchiWeb, :live_view
alias Mnishiguchi.Alchemists
@impl Phoenix.LiveView
def mount(_params, _session, socket) do
{:ok, assign(socket, alchemists: Alchemists.list_alchemists())}
end
@impl Phoenix.LiveView
def render(assigns) do
~L"""
<div class="row">
<%= for alchemist <- @alchemists do %>
<div class="card mb-3" data-role="alchemist-card" data-id="<%= alchemist.id %>">
<div class="card-body">
<h5 class="card-title"><%= alchemist.name %></h5>
</div>
<div>
<% end %>
</div>
"""
end
end
テストコードではdata-role
カスタム属性を用いてDOM要素を見つけられます。
defmodule MnishiguchiWeb.AlchemistsLiveTest do
use MnishiguchiWeb.ConnCase, async: true
import Phoenix.LiveViewTest
@path "/alchemists"
test "displays alchemists", %{conn: conn} do
_alchemist1 = create_alchemist(name: "Taro Yamada")
{:ok, view, _disconnected_html} = live(conn, @path)
assert has_alchemist_card?(view, "Taro Yamada")
refute has_alchemist_card?(view, "Jiro Yamada")
end
defp has_alchemist_card?(view, name) do
has_element?(view, "[data-role=alchemist-card]", name)
end
end
IDで特定したいのであれば、data-role
とdata-id
との2つのカスタム属性を用いてDOM要素を使用する手もあります。
defp has_alchemist_card?(view, id, name) do
has_element?(view, "[data-role=alchemist-card][data-id=#{id}]", name)
end
DOM要素のテキストにこだわらないのであれば、こういうので良い場合があるかもしれません。
defp has_alchemist_card?(view) do
has_element?(view, "[data-role=alchemist-card]")
end
Phoenix.LiveViewTestは、すばらしくawesomeです。
has_element?/3等の便利な関数を用いて、ヘッドレスブラウザーなしで動的コンテンツをテストできます。
open_browser/2でブラウザーを開いて今どんな状態なのかを確認することもできます。
最高に快適です。
その他のアプローチ
もちろん、世の中には他にもいろんなDOM要素を指定するCSS セレクターがあります。
よく使われるものの中にはこれらがあります。
data-test-id
はdata-role
に似ているのですが、明示的にテスト用属性なので少し意味合いが違います。テスト用途に限定せずにその要素のアプリでの振る舞いについて示す方が良いのではと考えています。
ARIAロールが使えるところはARIAロールを積極的に使うべきなのでしょう。Phoenixのデフォルトのフラッシュメッセージにはrole="alert"
が付与されています。
<main role="main" class="container my-4">
<p class="alert alert-info" role="alert"
phx-click="lv:clear-flash"
phx-value-key="info"><%= live_flash(@flash, :info) %></p>
<p class="alert alert-danger" role="alert"
phx-click="lv:clear-flash"
phx-value-key="error"><%= live_flash(@flash, :error) %></p>
<%= @inner_content %>
</main>
ですので、テストコードでそれが利用できます。
assert has_element?(view, "[role=alert]", ~s[No item matching "-1"])
しかしながら、ARIAロールは正式な仕様のようなので自由に好きなようにかけるものではありませんし、現時点ではまだ多くのロールの仕様がきまってない?ようです。それが難点です。
カスタムデータ属性を使用するメリット
- UIのスタイリング目的でHTMLタグ、CSSクラスやIDをイジっているときにテストが壊れるリスクの軽減
- DOM要素の役割を柔軟に表現できる
- 実は、属性セレクターには、いくつか便利なパターンマッチング構文があります
カスタムデータ属性を使用するデメリット
- 若干コードが冗長
- 一般的にはID セレクターの方がよく使われている
以上!
https://developer.mozilla.org/ja/docs/Web/HTML/Global_attributes/data-*