Finding DOM element by data-role in Phoenix LiveView testing

Today I put together one technique I find useful for writing test. That is to use data attributes for finding DOM elements in test. The same idea can apply to JavaScripting, but I focus mainly on Phoenix LiveView testing here because that is the one I have been working on.

TL;DR

As of writing, I prefer to take advantage of data attributes over using commonly used Id selectors. Particularly, I love the flexibility and explicitness of the custom data-role attribute.

Given that we have this HTML document

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

In test code, we can find a given DOM element by data-role.

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

In case we want to pinpoint a specific list item for example, we could use both data-role and data-id attributes.

  defp has_alchemist_card?(view, id, name) do
    has_element?(view, "[data-role=alchemist-card][data-id=#{id}]", name)
  end

Phoenix.LiveViewTest is so awesome that it allows us to test our dynamic pages easily without the need of a headless browser.

Alternative approaches

Of course, we can use other CSS selectors as well. Here are some commonly-used selectors.

The data-test-id is similar to data-role but the difference is to me data-test-id is less descriptive because it is test specific as opposed to data-role defining semantics of the DOM element itself.

While data-role is just a custom data attribute, ARIA roles are more formal standard or specifications and as of writing, details of many roles are to be determined.

Advantages of using data attributes

To me, some advantages of using data attributes are:

  • minimize the risk of accidentally breaking test code when styling pages
  • enable us to describe what the element is flexibly
  • make the DOM self explanatory

Disadvantages of using data attributes

I am aware of some disadvantages, but they are no big deal.

  • a little verbose
  • many examples out there might use DOM Id or class for finding an element

That's it!