目次
1.はじめに
2.自己紹介
3.開発環境
4.やりたいこと
5.困ったこと
6.解決策
7.おわりに
1.はじめに
Material-Uiのbuttonコンポーネントのクリックイベントのテストがつまったときに参考にできるものが少なかったので記事にしました!
今回はセレクトボックを例にしています。
2. 自己紹介
軽く自己紹介をします
- 大学3年生
- 経済学部
- 趣味は散歩、ゲーム(FPS)、アニメ、映画
3. 開発環境
- React 18.2.0
- TypeScript 4.8.2
- Jest 29.0.2
- Material-Ui
4. やりたいこと
やりたいこと:セレクトボックスをクリックしてセレクトオプションをクリックして選択できることを確かめたい
想定するユーザーアクション
- セレクトボックスをクリック
- 「テスト2」をクリック
想定するユーザーインターフェース
- セレクトボックスは「テスト1」が初期表示
- セレクトボックスがクリックされると「テスト1」「テスト2」を表示
- クリックされたセレクトオプションをセレクトボックスに表示
構成ファイルは以下になります。
import {useState} from "react";
import { Selection } from "./components/ui/Selection";
import { SelectChangeEvent } from "@mui/material/Select";
import "./App.css";
function App() {
const [test, setTest] = useState("test1");
const handleTest = (event: SelectChangeEvent) => {
setTest(event.target.value as string)
};
return (
<div className="App" style={{marginTop: 100+"px"}}>
<Selection selectValue={test} handleChangeEntry={(event) => handleTest(event)} />
</div>
);
}
export default App;
App.tsx
にセレクトボックスコンポーネントをimportして表示させています。
Selection.tsx
にはクリックイベント時に値を更新する関数とinput
のvalue
属性をpropsで渡してあげます。
import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import FormControl from "@mui/material/FormControl";
import Select, { SelectChangeEvent } from "@mui/material/Select";
type Selection = {
selectValue: string;
handleChangeEntry: (event: SelectChangeEvent) => void;
};
export const Selection: React.FC<Selection> = (props) => {
return (
<>
<FormControl sx={{ minWidth: 200 }}>
<InputLabel>テスト1/テスト2</InputLabel>
<Select
data-testid="select-value"
inputProps={{ required: true }}
labelId="select-value"
value={props.selectValue}
label="selectValue"
onChange={props.handleChangeEntry}
>
<MenuItem value={"test1"}>テスト1</MenuItem>
<MenuItem value={"test2"}>テスト2</MenuItem>
</Select>
</FormControl>
</>
);
};
Selection.tsx
はMaterial-Uiを使ってセレクトボックスUIを表示させています。
セレクトオプションには「テスト1」「テスト2」を置いています。
5. 困ったこと
よし!テストするぞ!と思い確認してみると、
うん??うまくレンダリングされていない??
イメージとしてはセレクトボックスを取得して、その後にセレクトオプションを取得し、選択してOKでした。
とても浅はかな考えですね(笑)
ソースコードは以下になります。
import App from "../../App";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";
describe("Selectionコンポーネント", () => {
describe("when select correctory", () => {
test("render correctory", async () => {
render(<App />);
userEvent.click(screen.getByTestId("select-value"));
await waitFor(() => {
fireEvent.change(screen.getByText("テスト1/テスト2"), {
target: { value: "テスト2" },
});
});
});
});
});
-
Select
をuserEvent.click
で取得 - 取得後、「テスト1/テスト/2」というテキストのあるタグを指定し、その中のセレクトオプション「テスト2」を選択
このようにすることでテストがうまくいくと思っていたのですが、テストで生成されたDOMを確認するするとMenuItem
の部分がうまくレンダリングされていないことがわかりました。
6. 解決策
問題はMenuItem
(セレクトオプション)の部分がレンダリングされていないこと
どうやったらレンダリングさせることができるのか???
その方法が以下になります。
import App from "../../App";
import { fireEvent, render, screen, within } from "@testing-library/react";
import "@testing-library/jest-dom";
describe("Selectionコンポーネント", () => {
describe("when select correctory", () => {
test("render correctory", async () => {
render(<App />);
const selectCompoEl = screen.getByTestId("select-value");
const button = within(selectCompoEl).getByRole(
"button"
) as HTMLInputElement;
fireEvent.mouseDown(button);
const listbox = within(screen.getByRole("presentation")).getByRole(
"listbox"
);
const options = within(listbox).getAllByRole("option");
fireEvent.click(options[1]);
expect(button).toHaveTextContent("テスト2");
});
});
});
...
一つずつ説明していきます。
-
selectCompoEl
にMUIのSelect
部分を代入const selectCompoEl = screen.getByTestId("select-value");
-
セレクトオプションを展開させるためにクリックイベントを発火させたいのでMUIの
Select
の子要素にあるbutton
ロールを持つ要素を取得し、button
に代入const button = within(selectCompoEl).getByRole("button") as HTMLInputElement;
-
上記で取得した要素を
mouseDown
するfireEvent.mouseDown(button);
-
セレクトオプション部分がレンダリングされるのでリストを囲っている部分を取得したいので
presentation
ロールを親に持つlistbox
ロールを持つ要素を取得し、listbox
に代入const listbox = within(screen.getByRole("presentation")).getByRole("listbox");
-
セレクトオプションを全て取得したいので
listbox
ロールを親に持つoption
ロールを持つ要素を取得し、options
に代入const options = within(listbox).getAllByRole("option");
-
今回は「テスト2」を選択するので
options
配列の2つ目の要素に対してクリックイベントfireEvent.click(options[1]);
-
セレクトボックスの値が「テスト2」と一致すればOK
expect(button).toHaveTextContent("テスト2");
7. おわりに
今回はReact✖️Material-Ui✖️Jestでclickイベントのテストをしているところを切り出してみました!
どなたかの役に立つものであると嬉しいです!
今後も機会があれば技術系の記事を書いてみようと思います。
参考にしたもの