🌸

Testing libraryでのgetBy, queryBy, findByの違いについて

2023/03/31に公開

getBy, queryBy, findByの違い

Testing Library には以下3種類のqueryがあります。
開発において、たびたびqueryの使い分けで混乱してしまうことがあったので、整理をこめて紹介します。

  • getBy
  • queryBy
  • findBy

それぞれ実装例と共に説明していきます。

getBy*

指定された条件に一致する単一の要素を返します。条件に一致する要素が見つからなかった場合、または複数のマッチが見つかった場合はエラーを返されます。
同じ条件で複数の要素を確認する場合は、代わりにgetAllByを使用します。

getByは特定の条件に一致する要素を取得するために有用です。

実装例

import { render, screen } from '@testing-library/react'

test('submitボタンが表示されていること', () => {
  render(<Submit />)
  const button = screen.getByRole('button', { name: 'submit' })
})

queryBy*

指定された条件に一致する単一の要素を返します。条件に一致する要素が見つからなかった場合は、nullが返されます
同じ条件で複数の要素を確認する場合は、代わりにqueryAllByを使用します。(queryAllByは条件に一致する要素が見つからなかった場合、空の配列([])を返します)

queryByは特定の要素が存在しないことを確認するために有用です。

実装例

import { render, screen } from '@testing-library/react'

test('cancelボタンが表示されていないこと', () => {
  render(<Submit />)
  expect(screen.queryByRole('button', { name: 'cancel' }).not.toBeInTheDocument();
})

findBy*

指定された条件に一致する単一の要素を解決するPromiseを返します。条件に一致する要素が見つからなかった場合、または1000ms後に複数の要素が見つかった場合はエラーを投げます。
同じ条件で複数の要素を確認する場合は、代わりにfindAllByを使用します。
findByはgetByクエリをwaitForでラップしたクエリーです。

実装箇所: "https://github.com/testing-library/dom-testing-library/blob/eadf7485430968df8d1e1293535d78cdbeea20a5/src/query-helpers.ts#L246-L248"

findByは特定の条件に一致する要素を非同期に取得するために有用です。
(非同期APIのレスポンスが帰ってきた後のコンテンツの表示や、ボタンをクリックした後のモーダル表示の確認など)
またfindByはPromiseを返すためasync/awaitかthen()を使用する必要があります。

実装例
非同期APIの呼び出し前、呼び出し後で変わる状態の確認

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import MyComponent from './MyComponent';

const server = setupServer(
  rest.get('/api/data', (req, res, ctx) => {
    return res(ctx.json({ data: 'dummy data' }));
  })
);

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

test('ボタンがAPIのレスポンスを待機している間はdisableになっていること', async () => {
  render(<MyComponent />);

  // ボタンがdisable状態であることを確認する
  const button = screen.getByRole('button', { name: 'Load Data' });
  expect(button).toBeDisabled();

  // ボタンをクリックする
  userEvent.click(button);

  // APIのレスポンスを待機する
  const dataElement = await screen.findByText('dummy data');

  // ボタンがenable状態になっていることを確認する
  expect(button).toBeEnabled();
});

まとめ

getBy

  • 特定の条件に一致する要素を取得するために有用
  • 存在しない要素を取得しようとするとエラーになる

queryBy

  • 特定の要素が存在しないことを確認するために有用
  • 存在しない要素を取得しようとするとnullを返す

findBy

  • 特定の条件に一致する要素を非同期に取得するために有用
  • 存在しない要素を取得しようとするとエラーになる
  • Promiseを返すためasync/awaitかthen()を使用する必要がある

ref

Discussion