問題 エラー内容
ローカルではテストが通るが、GitHub actionsではテストがエラーになる。
GitHub actionsのエラー
選択肢が描画されず、選択肢を選択できないためエラーになる
~~中略~~
FAIL src/__tests__/RegistrationPage.test.tsx (20.002 s)
TestingLibraryElementError: Value "1" not found in options
Ignored nodes: comments, script, style
<select
class="chakra-native-select__field css-1lldn8"
data-part="select"
data-scope="field"
data-testid="favorite-skill-select"
id=":r4:"
name="favoriteSkill"
>
<option
value=""
>
選択してください
</option>
</select>
66 | await waitFor(
67 | () => {
> 68 | user.selectOptions(selectElement, "1");
| ^
69 | },
70 | { timeout: 5000 },
71 | );
うまくいく場合の選択肢の描画(screen.debug)
debugで選択肢が表示される
<select
class="chakra-native-select__field css-1lldn8"
data-part="select"
data-scope="field"
data-testid="favorite-skill-select"
id=":r4:"
name="favoriteSkill"
>
<option
value=""
>
選択してください
</option>
<option
value="1"
>
React
</option>
<option
value="2"
>
TypeScript
</option>
<option
value="3"
>
GitHub
</option>
</select>
該当箇所
Register.tsx
<Field label="好きな技術 *">
<NativeSelectRoot>
<NativeSelectField
{...register("favoriteSkill", {
required: "好きな技術は必須入力です。",
})}
placeholder="選択してください"
data-testid="favorite-skill-select"
>
{skills.map((skill) => (
<option key={skill.id} value={skill.id}>
{skill.name}
</option>
))}
</NativeSelectField>
</NativeSelectRoot>
{errors.favoriteSkill && (
<Text color="red.500">{errors.favoriteSkill.message}</Text>
)}
</Field>
エラーになるテスト
RegistrationPage.test.tsx
import {
render,
screen,
fireEvent,
waitFor,
act,
} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
~~中略~~
await act(async () => {
const selectElement = await screen.getByTestId("favorite-skill-select");
await waitFor(() => {
userEvent.selectOptions(selectElement, "1");
}, { timeout: 5000 });
});
const registerButton = await waitFor(() =>
screen.getByTestId("register-button")
);
await act(async () => {
await waitFor(() => {
userEvent.click(registerButton);
});
});
await waitFor(() => {
expect(mockedUsedNavigate).toHaveBeenCalledWith("/");
}, { timeout: 5000 });
});
テスト修正後
RegistrationPage.test.tsx
import {
render,
screen,
fireEvent,
waitFor,
act,
} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
~~中略~~
// テストの冒頭で timeout を設定
jest.setTimeout(30000);
~~中略~~
// イベント発火はfireEventに変更している
await act(async () => {
fireEvent.change(selectElement, { target: { value: "1" } });
});
const registerButton = await waitFor(() =>
screen.getByTestId("register-button")
);
await waitFor(() => {
fireEvent.click(registerButton);
});
await waitFor(
() => {
expect(mockInsertUsers).toHaveBeenCalled();
expect(mockInsertUserSkill).toHaveBeenCalled();
expect(mockedUsedNavigate).toHaveBeenCalledWith("/");
},
{ timeout: 5000 }
);
await, waitFor, actの動作
await, waitFor, actについ(copilotの解説)
await:
Promiseが解決または拒否されるまで待機し、その結果を返します。
async 関数内でのみ使用できます。
waitFor:
非同期操作が完了するまで待機し、特定の条件が満たされるまでポーリングを行います。
主に、非同期で更新されるDOM要素の存在を確認するために使用されます。
act:
Reactの状態やエフェクトが更新される操作をラップし、その後のレンダリングが完了するまで待機します。
状態の変更やエフェクトの実行を伴う操作をテストするために使用されます。
おわりに
ローカルでは通るテストが、GitHub actionsでは通らなかったので困りました。
非同期処理は難しいので、どうすればテスト要件を満たせるのか考えるようにします。