升级Jest至28版本后,Formik+Yup表单验证测试失败
Jest 28升级后Formik+Yup表单校验测试失败问题
将Jest升级至28版本后,单元测试出现异常,尤其是验证Formik+Yup表单错误信息显示的测试用例全部失败。回退到Jest 27.5.1版本后,所有测试可正常运行。目前问题表现为:表单提交后,Formik与Yup的校验错误信息未正常渲染,导致测试断言失败。
依赖配置(package.json)
"dependencies": { "@chakra-ui/icons": "^1.0.16", "@chakra-ui/react": "^1.6.10", "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", "@types/styled-components": "^5.1.15", "axios": "^0.24.0", "formik": "^2.2.9", "framer-motion": "^4.1.17", "graphql": "^16.3.0", "graphql-request": "^4.0.0", "next": "^12.2.0", "next-i18next": "^10.2.0", "nookies": "^2.5.2", "react": "17.0.2", "react-dom": "17.0.2", "react-icons": "^4.3.1", "react-number-format": "^4.9.3", "react-query": "^3.34.6", "styled-components": "^5.3.3", "yup": "^0.32.11" }, "devDependencies": { "@testing-library/dom": "^8.16.0", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^12.1.5", "@testing-library/react-hooks": "^7.0.2", "@testing-library/user-event": "^13.5.0", "@types/node": "18.0.5", "@types/react": "17.0.33", "eslint": "7.32.0", "eslint-config-next": "12.0.1", "eslint-config-prettier": "^8.3.0", "jest": "^28.1.3", "jest-environment-jsdom": "^28.1.3", "msw": "^0.39.2", "react-test-renderer": "^17.0.2", "typescript": "4.4.4" }
Jest配置(jest.config.js)
// jest.config.js const nextJest = require('next/jest'); const createJestConfig = nextJest({ // Provide the path to your Next.js app to load next.config.js and .env files in your test environment dir: './', }); // Add any custom config to be passed to Jest const customJestConfig = { setupFilesAfterEnv: ['<rootDir>/jest.setup.js'], // if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work moduleNameMapper: { // Handle module aliases (this will be automatically configured for you soon) '^@/components/(.*)$': '<rootDir>/components/$1', '^@/pages/(.*)$': '<rootDir>/pages/$1', }, moduleDirectories: ['node_modules', '<rootDir>/'], testEnvironment: 'jsdom', }; // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async module.exports = createJestConfig(customJestConfig);
失败的测试代码
it('validates empty form values', async () => { server.use( graphql.query('GetAllProducts', (req, res, ctx) => { return res(ctx.status(200), ctx.data(getProductsResponseBody)); }) ); const signupResult = renderWithDefaultClient(<ProductPage />); await userEvent.click( await signupResult.findByText('addProductButtonLabel') ); await userEvent.click(await signupResult.findByText('createButtonLabel')); expect(await signupResult.findByText('required')).toBeInTheDocument(); });
被测组件代码
import { Button, FormControl, FormErrorMessage, FormLabel, HStack, Input } from '@chakra-ui/react'; import { Field, Form, Formik } from 'formik'; import { useTranslation } from 'next-i18next'; import React from 'react'; import * as Yup from 'yup'; import { FormProduct, IProduct } from '../../interfaces/pages/product/ProductInterface'; import { returnEmptyStringIfNull } from '../../utils/StringUtils'; const ProductForm = () => { const { t } = useTranslation(['product', 'shared']); const ProductFormSchema = Yup.object().shape({ code: Yup.string().max(45, t('characterLimitFortyFive', { ns: 'shared' })), name: Yup.string() .max(45, t('characterLimitFortyFive', { ns: 'shared' })) .required(t('required', { ns: 'shared' })), }); const initialValues: FormProduct = { code: returnEmptyStringIfNull(product.code), name: returnEmptyStringIfNull(product.name), }; const onSubmitForm = (productForm: FormProduct) => { createMutation.mutate(productForm); }; return ( <> <Formik initialValues={initialValues} validationSchema={ProductFormSchema} onSubmit={(form) => { onSubmitForm(form); }} > {(props) => ( <Form data-testid='productForm' style={{ marginTop: '20px' }}> <HStack spacing={'24px'} w={'2xl'} mb={3}> <Field name='code'> { // @ts-ignore ({ field, form }) => ( <FormControl isInvalid={form.errors.code && form.touched.code} > <FormLabel htmlFor='code'>{t('codeLabel')}</FormLabel> <Input {...field} id='code' data-testid='code' /> <FormErrorMessage>{form.errors.code}</FormErrorMessage> </FormControl> ) } </Field> <Field name='name'> { // @ts-ignore ({ field, form }) => { return ( <FormControl isInvalid={form.errors.name && form.touched.name} isRequired > <FormLabel htmlFor='name'>{t('nameLabel')}</FormLabel> <Input {...field} id='name' data-testid='name' /> <FormErrorMessage>{form.errors.name}</FormErrorMessage> </FormControl> ); } } </Field> </HStack> <HStack spacing={'24px'} mt={4}> <Button colorScheme='blue' type='submit'> {t('createButtonLabel', { ns: 'shared' })} </Button> </HStack> </Form> )} </Formik> </> ); }; export default ProductForm;
内容的提问来源于stack exchange,提问作者bojackhorseman99
相关产品推荐
相关产品推荐

