MUI Dialogテスト:基本から実践パターンまで網羅【React Testing Library】

MUI Dialogのテスト 基本から実践パターンまで React

MUI DialogをReact Testing Libraryでテストする方法を解説します。

開閉制御、ESCキー・バックドロップクリックでの閉じる動作など、実務で必要なパターンを網羅しています。

動作環境

  • @mui/material v6
  • @testing-library/react v16
  • @testing-library/user-event v14
  • vitest v3

基本的なテスト

Dialogの取得

MUI Dialogはdialogロールを持ちます。getByRole('dialog')で取得できます。

import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import DialogActions from '@mui/material/DialogActions';
import Button from '@mui/material/Button';
import {render, screen} from '@testing-library/react';

const BasicDialog = ({open, onClose}: {open: boolean; onClose: () => void}) => {
  return (
    <Dialog open={open} onClose={onClose}>
      <DialogTitle>タイトル</DialogTitle>
      <DialogContent>コンテンツ</DialogContent>
      <DialogActions>
        <Button onClick={onClose}>閉じる</Button>
      </DialogActions>
    </Dialog>
  );
};

test('Dialogが表示される', () => {
  render(<BasicDialog open={true} onClose={vi.fn()} />);
  
  const dialog = screen.getByRole('dialog');
  expect(dialog).toBeInTheDocument();
});

開閉制御(open prop)

test('open=false の場合、Dialogが表示されない', () => {
  render(<BasicDialog open={false} onClose={vi.fn()} />);
  
  const dialog = screen.queryByRole('dialog');
  expect(dialog).not.toBeInTheDocument();
});

test('open=true の場合、Dialogが表示される', () => {
  render(<BasicDialog open={true} onClose={vi.fn()} />);
  
  const dialog = screen.getByRole('dialog');
  expect(dialog).toBeInTheDocument();
});

rerenderで開閉をテスト

MUIのDialogはアニメーションがあるため、閉じる際はwaitForで待機します。

import {waitFor} from '@testing-library/react';

test('openをfalseに変更するとDialogが閉じる', async () => {
  const onClose = vi.fn();
  const {rerender} = render(<BasicDialog open={true} onClose={onClose} />);

  // 最初は表示されている
  expect(screen.getByRole('dialog')).toBeInTheDocument();

  // openをfalseに変更
  rerender(<BasicDialog open={false} onClose={onClose} />);

  // アニメーション完了を待つ
  await waitFor(() => {
    expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
  });
});

タイトル・コンテンツの確認

test('タイトルが表示される', () => {
  render(<BasicDialog open={true} onClose={vi.fn()} />);
  
  expect(screen.getByText('タイトル')).toBeInTheDocument();
});

test('コンテンツが表示される', () => {
  render(<BasicDialog open={true} onClose={vi.fn()} />);
  
  expect(screen.getByText('コンテンツ')).toBeInTheDocument();
});

閉じる操作のテスト

ボタンクリックで閉じる

import userEvent from '@testing-library/user-event';

test('閉じるボタンをクリックするとonCloseが呼ばれる', async () => {
  const user = userEvent.setup();
  const onClose = vi.fn();
  
  render(<BasicDialog open={true} onClose={onClose} />);

  await user.click(screen.getByRole('button', {name: '閉じる'}));

  expect(onClose).toHaveBeenCalledTimes(1);
});

ESCキーで閉じる

test('ESCキーを押すとonCloseが呼ばれる', async () => {
  const user = userEvent.setup();
  const onClose = vi.fn();
  
  render(<BasicDialog open={true} onClose={onClose} />);

  await user.keyboard('{Escape}');

  expect(onClose).toHaveBeenCalledTimes(1);
});

バックドロップクリックで閉じる

test('バックドロップをクリックするとonCloseが呼ばれる', async () => {
  const user = userEvent.setup();
  const onClose = vi.fn();
  
  render(<BasicDialog open={true} onClose={onClose} />);

  const backdrop = document.querySelector('.MuiBackdrop-root');
  expect(backdrop).toBeInTheDocument();

  if (backdrop) {
    await user.click(backdrop);
  }

  expect(onClose).toHaveBeenCalledTimes(1);
});

test('Dialog内をクリックしてもonCloseは呼ばれない', async () => {
  const user = userEvent.setup();
  const onClose = vi.fn();
  
  render(<BasicDialog open={true} onClose={onClose} />);

  await user.click(screen.getByText('コンテンツ'));

  expect(onClose).not.toHaveBeenCalled();
});

disableEscapeKeyDownの動作

ESCキーでの閉じる動作を無効化できます。

const DialogWithEscapeDisabled = ({
  open,
  onClose,
  disableEscapeKeyDown,
}: {
  open: boolean;
  onClose: () => void;
  disableEscapeKeyDown: boolean;
}) => {
  return (
    <Dialog open={open} onClose={onClose} disableEscapeKeyDown={disableEscapeKeyDown}>
      <DialogTitle>タイトル</DialogTitle>
      <DialogContent>コンテンツ</DialogContent>
    </Dialog>
  );
};

test('disableEscapeKeyDown=true の場合、ESCキーでonCloseが呼ばれない', async () => {
  const user = userEvent.setup();
  const onClose = vi.fn();
  
  render(
    <DialogWithEscapeDisabled
      open={true}
      onClose={onClose}
      disableEscapeKeyDown={true}
    />
  );

  await user.keyboard('{Escape}');

  expect(onClose).not.toHaveBeenCalled();
});

test('disableEscapeKeyDown=false の場合、ESCキーでonCloseが呼ばれる', async () => {
  const user = userEvent.setup();
  const onClose = vi.fn();
  
  render(
    <DialogWithEscapeDisabled
      open={true}
      onClose={onClose}
      disableEscapeKeyDown={false}
    />
  );

  await user.keyboard('{Escape}');

  expect(onClose).toHaveBeenCalledTimes(1);
});

まとめ

この記事では、MUI DialogをReact Testing Libraryでテストする方法を解説しました。

テストパターン一覧

パターン使用するクエリ/マッチャー
Dialogの取得getByRole('dialog')
非表示の確認queryByRole('dialog') + not.toBeInTheDocument()
ボタンクリックuserEvent.click()
ESCキーuserEvent.keyboard('{Escape}')
バックドロップdocument.querySelector('.MuiBackdrop-root')
アニメーション待機waitFor()

ポイント

  1. DialogはgetByRole('dialog')で取得、非表示確認はqueryByRoleを使う
  2. MUIのアニメーションがあるため、閉じる際はwaitForで待機
  3. ESCキーはuserEvent.keyboard('{Escape}')でテスト
  4. バックドロップはdocument.querySelector('.MuiBackdrop-root')で取得

コメント

タイトルとURLをコピーしました