🧪

React Testing LibraryでfireEventとuser-eventを比較する

に公開

React Testing LibraryでfireEventuser-eventのどちらを使うべきか、実際の違いを検証しながら解説します。

両者の概要

どちらもReact Testing Libraryに含まれるテスト用関数です。

fireEvent

DOMイベントを直接発火させる関数です。

import { fireEvent } from '@testing-library/react'

fireEvent.click(screen.getByRole('button'))

user-event

ユーザーの実際の操作をシミュレートする関数です。

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

userEvent.click(screen.getByRole('button'))

公式の見解

Most projects have a few use cases for fireEvent, but the majority of the time you should probably use @testing-library/user-event.

: fireEventの使用例もありますが、大半の場合はuser-eventを使用すべきです。

出典: https://testing-library.com/docs/dom-testing-library/api-events/

違い1: 記述の簡潔さ

user-eventの方がより直感的に書けます。

import { fireEvent } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

// fireEvent: イベントオブジェクトを手動で構築
fireEvent.change(screen.getByRole('textbox'), { target: { value: 'test' } })

// user-event: 実際の入力をシミュレート
userEvent.type(screen.getByRole('textbox'), 'test')

違い2: ユーザー操作の忠実性(重要)

user-eventは実際のユーザー操作を忠実に再現し、fireEventでは検出できないバグを見つけられます。

検証: 非活性フォームのテスト

名前入力フォームで、非活性時は入力も表示もされないことを確認します。

コンポーネント:

type Props = {
  isDisabled?: boolean;
}

const SampleComponent: FC<Props> = ({ isDisabled = false }) => {
  const [name, setName] = useState("");

  return (
    <div>
      <input
        type="text"
        placeholder="Enter your name"
        value={name}
        onChange={(e) => setName(e.target.value)}
        disabled={isDisabled}
      />
      <p>{name}</p>
    </div>
  );
}

fireEventの場合(問題あり)

test("フォーム非活性の場合、入力できず値も表示されない", () => {
  const { getByRole, queryByText } = render(<SampleComponent isDisabled />);

  const input = getByRole("textbox");
  expect(input).toBeDisabled();
  
  // ❌ 非活性でも入力値を変更できてしまう
  fireEvent.change(input, { target: { value: "John" } });
  
  // ❌ 本来表示されないはずが、テストが通ってしまう
  expect(queryByText("John")).toBeInTheDocument();
})

問題点: fireEventはDOMイベントを直接発火するため、非活性状態を無視して値を変更できてしまいます。

user-eventの場合(正しい)

test("フォーム非活性の場合、入力できず値も表示されない", async () => {
  const user = userEvent.setup();
  const { getByRole, queryByText } = render(<SampleComponent isDisabled />);

  const input = getByRole("textbox");
  expect(input).toBeDisabled();
  
  // ✅ ユーザーが入力しようとするが、非活性のため入力できない
  await user.type(input, "John");
  
  // ✅ 正しく表示されない
  expect(queryByText("John")).toBeNull();
})

利点: user-eventは実際のユーザー操作を再現するため、非活性要素には入力できません。

まとめ

user-eventを使うべき理由:

  1. より実際のユーザー操作に近いテストが書ける
  2. ブラウザの制約を考慮した検証ができる
  3. 記述がシンプルで直感的

fireEventが適している場合:

  • 非常に低レベルなDOMイベントのテスト
  • user-eventで実現できない特殊なケース

推奨: 基本的にはuser-eventを使用し、特別な理由がない限りfireEventは避けましょう。より信頼性の高いテストが書けます。

Discussion