はじめに
jestでtestを行っている際に、関数が実行されず、テストが失敗しました。
解決まで、時間をとられたので解決策をまとめようと思います。
事象
新規登録ページを作成し、そのテストを行いました。
すると以下のエラーが発生しました。
expect(jest.fn()).toHaveBeenCalledWith(...expected)
Expected: "山田太郎", "1"
Number of calls: 0
期待する引数と、受け取った引数があっていないというエラーです。
該当コード
コード一部抜粋
<form onSubmit={handleSubmit(onSubmit)}>
<FormControl mb={4} isInvalid={!!errors.description}>
<FormLabel>名前 <span style={{ color: 'red' }}>*</span></FormLabel>
<Textarea
placeholder="山田太郎"
{...register("name", {
required: "必須項目です"
})} />
<FormErrorMessage>{errors.description?.message}</FormErrorMessage>
</FormControl>
<FormControl mb={4} isInvalid={!!errors.select}>
<FormLabel>セレクトボックス <span style={{ color: 'red' }}>*</span></FormLabel>
<Select
placeholder='選択してください'
{...register("select", {
required: "必須項目です"
})} >
{selects?.map((select) => (
<option key={select.id} value={select.id}>{select.name}</option>
))}
</Select>
<FormErrorMessage>{errors.select?.message}</FormErrorMessage>
</FormControl>
<Button isLoading={loading} type="submit" colorScheme='teal' data-testid="submit" mx={4} mb={4}>登録</Button>
</form>
このテストを実行しました。
fireEvent.changeで値を入力、fireEvent.clickでsubmitボタンをクリック。
it("登録できること", async () => {
render(
<CardRegister />
);
fireEvent.change(screen.getByLabelText('名前', { exact: false }), { target: { value: '山田太郎' } });
fireEvent.change(screen.getByLabelText('セレクトボックス', { exact: false }), { target: { value: '1' } });
fireEvent.click(screen.getByTestId('submit'));
await waitFor(() => {
expect(addUser).toHaveBeenCalledWith(
'山田太郎',
'1',
);
});
原因
原因は簡単で、入力がうまくできておらず、バリデーションエラーで引っかかっていました。
セレクトボックスをsupabaseというDBから、非同期でデータを取得しているのですが、データ取得前にfireEvent.changeを入力してしまっていました。
value: '1'はその段階では存在しないので、選択されず、バリデーションエラーが発生しました。
フォーム項目を非同期で取得していることを失念していました、、
解決策
セレクトボックスのフォームにdata-testidを追加
<FormLabel>セレクトボックス <span style={{ color: 'red' }}>*</span></FormLabel>
<Select
placeholder='選択してください'
data-testid="select" // 追加
{...register("select", {
required: "必須項目です"
})} >
{selects?.map((select) => (
<option key={select.id} value={select.id}>{select.name}</option>
))}
</Select>
<FormErrorMessage>{errors.select?.message}</FormErrorMessage>
入力を非同期に変更
findByTestIdで非同期取得にして、awaitをつけてあげます。
it("登録できること", async () => {
render(
<CardRegister />
);
fireEvent.change(screen.getByLabelText('名前', { exact: false }), { target: { value: '山田太郎' } });
fireEvent.change(await screen.findByTestId('select'), { target: { value: "1" } });
fireEvent.click(screen.getByTestId('submit'));
await waitFor(() => {
expect(addUser).toHaveBeenCalledWith(
'山田太郎',
'1',
);
});
まとめ
toHaveBeenCalledWithでエラーが起きたら、モックができていないか、非同期部分でバリデーションエラーにひっかかってないか疑うのが良いでしょう。