React Testing Libraryでrole属性が定義されていないHTMLタグを取得したい

背景

React Testing Libraryでテストを書くとき、たまに<span><div>などrole属性が設定されていないタグを取得したい場面がある。Testing Libraryのクエリはアクセシビリティを意識したものになっているため、<div>のようにrole属性がないタグをそのまま取得する手段は少ない。

そもそも可能な限りTesting Libraryのクエリで取得可能なHTML構成・テストケースを検討するべきだが、どうしても難しいときもある。かといってクエリを書くためだけにrole属性をつけてしまうと本当に必要な人が困ってしまう。読み上げリーダーやキーボード操作はrole属性を参照するためである。本来の意図からずれたrole属性はWebページの意図を正しく伝えることができない。よって避けたい。

取得方法

次の方法を使うことで、role属性を付与せずテストを実行できる。

  1. Document.querySelector()を使ってcontainer要素から指定のHTMLタグを取得する
  2. jest-domのmatcherを使ってテストする
import React from "react";
import StarRating from "../StarRating";

export interface Colors {
  id: string;
  title: string;
  color: string;
  rating: number;
}

export default function Color({ id, title, color, rating }: Colors) {
  return (
    <section>
      <h1>{title}</h1>
      <span>色を選ぶ</span>
      <div style={{ height: 50, backgroundColor: color }}></div>
    </section>
  );
}
import React from "react";
import Color from ".";
import { render } from "@testing-library/react";

// 可能ならこちらの方法で取得したい
it("色を選ぶテキストがレンダリングされていること", () => {
  const content = render(<Color id="1" title="red" color="#f00" rating={3} />);
  expect(content.getByText("色を選ぶ")).toBeInTheDocument();
});

// spanタグのようにrole属性が存在しないHTMLタグを取得する場合
it("spanタグの中に色を選ぶテキストがレンダリングされていること", () => {
  const content = render(<Color id="1" title="red" color="#f00" rating={3} />);
  // spanタグ要素を取得
  const span = content.container.querySelector("span");
  // 取得した要素の中にテキストが存在すること
  expect(span).toHaveTextContent("色を選ぶ");
});

説明など

@testing-library/reactrender関数で取得したコンテンツからRenderResult型の要素を取得できる。

export function render(
  ui: React.ReactElement,
  options?: Omit<RenderOptions, 'queries'>,
): RenderResult

取得結果のcontainerを見ると、Document.querySelector()が使える型で値が取得できる。

export type RenderResult<
  Q extends Queries = typeof queries,
  Container extends Element | DocumentFragment = HTMLElement, →ElementかDocumentFragmentのいずれかになる or HTMLElementが適用される
  BaseElement extends Element | DocumentFragment = Container,
> = {
  container: Container
  // 省略
}

Document.querySelector()はid・class・任意のHTMLタグを取得できる。今回は任意のHTMLタグを取得している。

…という具合でクエリが書ける。型が変わるため、さらにTesting Libraryのクエリを書けない点は注意が必要。 また、Document.querySelector()はnullが推論される点も注意が必要。

// HTMLSpanElementとして推論されているためTesting Libraryのクエリは使えない。
expect(span).getByText("色を選ぶ");

知っていればできるが、ここに辿り着かないとなかなか書けないなと思った。

参考文献