🧪
React Testing LibraryでfireEventとuser-eventを比較する
React Testing LibraryでfireEventとuser-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を使うべき理由:
- より実際のユーザー操作に近いテストが書ける
- ブラウザの制約を考慮した検証ができる
- 記述がシンプルで直感的
fireEventが適している場合:
- 非常に低レベルなDOMイベントのテスト
-
user-eventで実現できない特殊なケース
推奨: 基本的にはuser-eventを使用し、特別な理由がない限りfireEventは避けましょう。より信頼性の高いテストが書けます。
Discussion