実現したいこと
エラーを理解し、テストの第一歩としてレンダリングを成功させたい。
前提
React学習のためのアプリの制作が終わり、勉強を兼ねてテスト作成に取り組んでいます。
簡単なコンポーネントに対してのテストは書けたものの、少し複雑なPropsを渡す必要のあるコンポーネントに対してテストを行おうとしたところ、エラーが発生。
テスト時は私の認識不足でいつもエラーが発生はするのですが、調べたり試行錯誤することで解消出来ていたのですが、今回、解消が出来なかったため質問させていただきました。
おそらくですが、ReactHookFromのRegisterをmock化する方法が間違えているのだと思うのですが何をテストに対しても知識が浅いため、どのようにmock化すればエラーが解消できるのかがわかりませんでした。
発生している問題・エラーメッセージ
以下、テストの結果を貼り付けます。
FAIL src/__Test__/molecules/OkaimonoDetail.test.tsx OkaimonoDetail ✕ テスト中 (91 ms) ● OkaimonoDetail › テスト中 TypeError: Cannot destructure property 'ref' of 'register(...)' as it is undefined. 90 | {fields.map((field, index) => { 91 | const { > 92 | ref, | ^ 93 | onChange: registerOnChange, 94 | ...rest 95 | } = register(`listForm.${index}.purchaseName`, {
該当のソースコード
■コンポーネントのコード import { SmallAddIcon } from "@chakra-ui/icons"; import { Box, ComponentWithAs, Divider, FormLabel, Heading, HStack, Icon, IconProps, Input, InputGroup, InputRightElement, Text, VStack, } from "@chakra-ui/react"; import { ListFormParams, MergeParams } from "interfaces"; import React, { Dispatch, memo, SetStateAction, VFC } from "react"; import { FieldArrayWithId, FieldErrors, FieldValues, UseFieldArrayRemove, UseFormGetValues, UseFormRegister, UseFormSetValue, UseFormWatch, } from "react-hook-form"; type Props = { fields: FieldArrayWithId<MergeParams, "listForm", "key">[]; // eslint-disable-next-line no-unused-vars insertInputForm: (index: number) => void; SmallCloseIcon: ComponentWithAs<"svg", IconProps>; remove: UseFieldArrayRemove; register: UseFormRegister<MergeParams>; errors: FieldErrors<MergeParams>; validationNumber: RegExp; readOnly?: boolean; getValues?: UseFormGetValues<MergeParams>; deleteIds?: string[]; setDeleteIds?: Dispatch<SetStateAction<string[]>>; watch: UseFormWatch<FieldValues>; expiryDate?: boolean; // eslint-disable-next-line no-unused-vars onListChange?: (event: React.ChangeEvent<HTMLInputElement>, index: number, newValue: string) => void; purchaseNameSuggestions?: ListFormParams[]; setValue?: UseFormSetValue<MergeParams>; setPurchaseNameSuggestions?: React.Dispatch<React.SetStateAction<ListFormParams[]>>; purchaseNameIndex?: number; }; export const OkaimonoDetail: VFC<Props> = memo((props) => { const { fields, insertInputForm, SmallCloseIcon, remove, register, errors, validationNumber, readOnly = false, getValues, setDeleteIds, watch, expiryDate, onListChange, purchaseNameSuggestions, setValue, setPurchaseNameSuggestions, purchaseNameIndex, } = props; const onClickSuggests = ( event: React.MouseEvent<HTMLParagraphElement, MouseEvent>, purchaseName: string, index: number ) => { event.preventDefault(); if (setValue && setPurchaseNameSuggestions && purchaseName) { setValue(`listForm.${index}.purchaseName`, purchaseName); setPurchaseNameSuggestions([]); } }; return ( <Box> <Heading as="h3" size="sm" textAlign="center" pt={1} pb={3}> お買い物リスト </Heading> {fields.map((field, index) => { const { ref, onChange: registerOnChange, ...rest } = register(`listForm.${index}.purchaseName`, { required: { value: true, message: "商品名が入力されていません" }, maxLength: { value: 35, message: "最大文字数は35文字までです。" }, }); const customOnChange = (event: React.ChangeEvent<HTMLInputElement>) => { // 親コンポーネントから渡された onChange ハンドラを実行 if (onListChange) { onListChange(event, index, event.target.value); } // 入力が空の場合、候補リストをクリアする if (setPurchaseNameSuggestions && event.target.value === "") { setPurchaseNameSuggestions([]); } // React Hook Form の onChange ハンドラを実行 if (registerOnChange) { registerOnChange(event); } }; const startDate = watch(`listForm.${index}.expiryDateStart`); return ( <HStack key={field.key} px={2} py={3} w="100%" bg="white" rounded="xl" mb="2"> <VStack spacing={1} w="5%"> <Box display={fields.length < 20 || readOnly ? "block" : "none"}> <SmallAddIcon bg="teal.500" rounded="full" color="white" onClick={(event) => { if (readOnly) { event.preventDefault(); // eslint-disable-next-line no-alert alert("確認画面では使用できません。"); return; } insertInputForm(index); }} /> </Box>
■ユニットテストのコード
import { SmallCloseIcon } from "@chakra-ui/icons"; import { render, RenderResult } from "@testing-library/react"; import { OkaimonoDetail } from "components/molecules/OkaimonoDetail"; describe("OkaimonoDetail", () => { const getValuesMock = jest.fn().mockReturnValue(99); //getValuesを再現するために記述。 const testPurchaseObjs = [ { userId: "test1", shopId: "shopId", id: "1", asc: "0", purchaseName: "testPurchase1", price: "999", shoppingDetailMemo: "testMemo", amount: "amount", shoppingDate: "2023-07-27", expiryDateStart: "2023-07-28", expiryDateEnd: "2023-07-29", listId: "99", isBought: true, isFinish: true, differentDay: 1, isDelete: false, memosCount: 3, totalBudget: 9999, }, { userId: "test2", shopId: "shopId", id: "2", asc: "1", purchaseName: "testPurchase2", price: "999", shoppingDetailMemo: "testMemo", amount: "amount", shoppingDate: "2023-07-27", expiryDateStart: "2023-07-28", expiryDateEnd: "2023-07-29", listId: "99", isBought: true, isFinish: true, differentDay: 1, isDelete: false, memosCount: 3, totalBudget: 9999, }, ]; const FieldMocks = [ { amount: "", asc: "", expiryDateEnd: "", expiryDateStart: "", id: "", key: "43ea4b2c-54a6-465b-9ae8-25e68eb94dc9", price: "", purchaseName: "", shoppingDetailMemo: "" } ]; const mockFunctions = { fields: FieldMocks, insertInputForm: jest.fn(), SmallCloseIcon, remove: jest.fn(), register: jest.fn().mockReturnValue({ ref: jest.fn(), onChange: jest.fn(), name: 'listForm', }), errors: {}, validationNumber: /^[0-9]+$/, readOnly: false, getValues: getValuesMock, setDeleteIds: jest.fn(), watch: jest.fn(), expiryDate: false, onListChange: jest.fn(), purchaseNameSuggestions: testPurchaseObjs, setValue: jest.fn(), setPurchaseNameSuggestions: jest.fn(), purchaseNameIndex: 0, }; let utils: RenderResult; beforeEach(() => { utils = render(<OkaimonoDetail {...mockFunctions} />); }); test("テスト中", () => { console.log(mockFunctions.register.mock.calls); }); });
試したこと
上記のテストコードの通り、console.logを記載し、エラーの特定を行いました。
しかし、以下のような結果が返されてしまいます。
console.error
The above error occurred in one of your React components:
at /Users/apple/Github/myportfolio03/frontend/src/components/molecules/OkaimonoDetail.tsx:55:5 Consider adding an error boundary to your tree to customize error handling behavior. Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
55行目はコンポーネントのconst内の一番上のfieldsに該当するため、テストコード内のFieldMocksを見直してみたのですが、誤りらしい部分を見つけられませんでした。
どうか、エラーの原因特定にお力をお貸しいただけないでしょうか。
補足情報(FW/ツールのバージョンなど)
"react-hook-form": "^7.43.9",
"react": "^17.0.2",
"@chakra-ui/react": "^1.0.4",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^12.0.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",打ち消し線

回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2023/08/03 08:19
2023/08/03 09:36