You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 22:45:39