テスト実装例
- コンポーネントには、ユーザー名を表示するテキスト要素、ユーザー名を変更するためのテキストフィールド、およびユーザー名を更新するボタンがあります。
- テキストフィールドに新しいユーザー名を入力し、ボタンをクリックすると、テキスト要素のユーザー名が更新されます。
上記についてテストをしてみる
describe('UserProfileコンポーネント', () => {
test('ユーザー名が正しく更新されるかどうかをテストする', async () => {
// テスト用のモック関数を定義する
const updateUsernameMock = jest.fn();
// コンポーネントをレンダリングする
render(<UserProfile username="John Doe" onUpdateUsername={updateUsernameMock} />);
// テキスト要素を取得する
const usernameElement = screen.getByText('John Doe');
// テキストフィールドを取得する
const textFieldElement = screen.getByLabelText('ユーザー名');
// テキストフィールドに新しいユーザー名を入力する
userEvent.clear(textFieldElement);
userEvent.type(textFieldElement, 'Jane Smith');
// 更新ボタンをクリックする
userEvent.click(screen.getByText('更新'));
// テキスト要素のユーザー名が更新されるのを待機する
await screen.findByText('Jane Smith');
// updateUsernameMockが適切に呼び出されたかを検証する
expect(updateUsernameMock).toHaveBeenCalledWith('Jane Smith');
});
});
メソッドの使い分け
-
findByText
- 非同期の更新を待つために使用します。
- データの取得や非同期の処理が行われ、描画が完了するのを待つ必要がある場合に使用します。
-
getByText
- 描画されている要素を直接確認するために使用します。
- 既存の要素がブラウザに描画されているかを確認する場合に使用します。
-
queryByText
- 要素の存在を確認するために使用します。
- 要素が存在するかどうかを確認したい場合に使用します。
get 検索
検索方法 | 使い方 | 例 |
---|---|---|
テキストの検索 | getByText("テキスト") | <div>テキスト</div> |
ラベルによる検索 | getByLabelText("ラベル") | <label htmlFor="input">ラベル</label> <input id="input" /> |
CSSセレクタによる検索 | container.querySelector(".my-class") | <div class="my-class">要素</div> |
属性による検索 | getByAttribute("data-testid", "my-element") | <div data-testid="my-element">要素</div> |
テキストフィールドの値の検索 | getByDisplayValue("テキスト") | <input type="text" value="テキスト" /> |
find 検索
検索方法 | 使い方 | 例 |
---|---|---|
findByText |
await screen.findByText(text) |
await screen.findByText("テキスト") |
findByLabelText |
await screen.findByLabelText(text) |
await screen.findByLabelText("ラベル") |
findBySelector |
await screen.findBySelector(selector) |
await screen.findBySelector(".my-class") |
findByAttribute |
await screen.findByAttribute(attribute) |
await screen.findByAttribute("data-testid") |
findByDisplayValue |
await screen.findByDisplayValue(value) |
await screen.findByDisplayValue("テキスト") |
これらのfindBy
メソッドは、非同期操作が完了するまで待機し、要素を検索します。非同期操作が完了した後、要素が見つかるとPromiseが解決されます。要素が見つからなかった場合、指定されたタイムアウト時間を超過してもPromiseは拒否されます。
await
を使用する。
useStateの処理のテストは大体findを使用する(setStateは非同期らしい)
これらのfindBy
メソッドは、テスト中に非同期操作が発生する場合や非同期でDOMの変更が行われる場合に役立ちます。
userEventの使い方
メソッド | 説明 |
---|---|
userEvent.click(element) |
要素をクリックします。 |
userEvent.type(element, text) |
要素にテキストを入力します。 |
userEvent.clear(element) |
テキスト入力要素の値をクリアします。 |
userEvent.selectOptions(element, value) |
セレクトボックスのオプションを選択します。 |
userEvent.tab() |
キーボードのTabキーを押します。 |
これらの操作は、テスト中にユーザーが実際に行う操作をシミュレートするために使用されます。例えば、テキストフィールドにテキストを入力したり、ボタンをクリックしたり、セレクトボックスのオプションを選択したりすることができます。
userEvent
を使用する際には、非同期操作やDOMの変更に対して適切な待機処理を組み合わせることが重要です。待機処理には、waitFor
やact
などの適切な手段を使用して、操作が完了し、期待される結果が反映されるのを待つ必要があります。
これらの関数を組み合わせて使用することで、より実際のユーザーエクスペリエンスに近い形でテストを実行することができます。
以下に、共有していただいたマッチャーをテーブルにまとめました。
expect マッチャー
マッチャー | 説明 |
---|---|
toBeTruthy() |
値が真の場合にパスします。 |
toBeFalsy() |
値が偽の場合にパスします。 |
toBeNull() |
値が null の場合にパスします。 |
toBeDefined() |
値が undefined でない場合にパスします。 |
toBeUndefined() |
値が undefined の場合にパスします。 |
toBeNaN() |
値が NaN の場合にパスします。 |
toBeGreaterThan(value) |
値が value より大きい場合にパスします。 |
toBeGreaterThanOrEqual(value) |
値が value 以上の場合にパスします。 |
toBeLessThan(value) |
値が value より小さい場合にパスします。 |
toBeLessThanOrEqual(value) |
値が value 以下の場合にパスします。 |
toContain(item) |
配列や文字列が item を含んでいる場合にパスします。 |
toMatch(pattern) |
値が正規表現 pattern にマッチする場合にパスします。 |
toThrow(error) |
関数が error をスローする場合にパスします。 |
テストの基本の形
describe("Review Page", () => {
test("renders default page text", async () => {
render(
<Provider>
<Page />
</Provider>
);
// テキストが正しく表示されることを検証するアサーション
const reviewText = screen.getByText("This is the default page for REVIEW");
expect(reviewText).toBeInTheDocument();
// 特定の要素が存在することを検証するアサーション
const button = screen.getByRole("button", { name: "Submit" });
expect(button).toBeInTheDocument();
// クリックイベントをシミュレートして、期待される結果を検証するアサーション
userEvent.click(button);
const successMessage = await screen.findByText("Review submitted successfully");
expect(successMessage).toBeInTheDocument();
});
});
1. describeでテストスイーツ(グループ)を作成する
テストスイート名は、テストする機能やユースケースに関連したもの
describe("Calculator functionality", () => {
// テストケースやアサーションを記述する
});
2. test関数でtestを定義
テストケース名は、テストの目的や期待される結果を反映するように具体的に命名
test("should subtract two numbers correctly", () => {
// テストの内容を記述
});
test("should multiply two numbers correctly", () => {
// テストの内容を記述
});
3. renderでテストしたいコンポーネントをrenderする
テスト対象のコンポーネントをrender関数でレンダリングします
render(
<Provider>
<Page />
</Provider>
);
4. アサーション(expect) を追加してテストの内容を検証する
レンダリング後のコンポーネントに対して、期待される結果を検証するアサーションを追加します。
// テキストが正しく表示されることを検証するアサーション
const reviewText = screen.getByText("This is the default page for REVIEW");
expect(reviewText).toBeInTheDocument();
// 特定の要素が存在することを検証するアサーション
const button = screen.getByRole("button", { name: "Submit" });
expect(button).toBeInTheDocument();
// クリックイベントをシミュレートして、期待される結果を検証するアサーション
userEvent.click(button);
const successMessage = await screen.findByText("Review submitted successfully");
expect(successMessage).toBeInTheDocument();
以上が基本的なテストを記述する順序🐇
Jotaiで管理したコンポーネントのテスト方法
コンポーネントをrenderする際にはProviderを使用すること
Jotaiの状態を参照したり更新したりするコンポーネントは、
適切な状態を提供するためにProviderコンポーネントが必要
describe("Sales test", () => {
test("renders the Sales text", async () => {
render(
<Provider>
<Sales />
</Provider>
);
});