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

使用Jest与React Testing Library测试Formik错误提示组件遇阻

问题:测试Formik异步表单时,无法捕获用餐人数的错误提示

问题描述

我在测试Web应用时,需要验证点击continue按钮后,页面是否会渲染用餐人数选项的错误提示。由于Formik表单是异步的,我已经添加了async、await和waitFor来确保状态更新,但仍然报错:无法找到包含文本“ Please Select the Amount of Diners”的元素*。

测试代码

test('it shows 4 required fields', async () => {
    const user = userEvent.setup()
    
    user.click(screen.getByTestId('continue'))
    
    const diners = await screen.findByText('* Please Select The Amount of Diners');

    await waitFor(() => {
         expect(diners).toBeInTheDocument('* Please Select The Amount of Diners');
    })
})

Formik表单用餐人数部分代码

<Field 
     label="NoOfDiners" 
     className= {errors.NoOfDiners && touched.NoOfDiners ? "input--error": "form--button"}
     as="select" name="NoOfDiners" 
     placeholder="NumberOfDiners" 
     data-testid="NoOfDiners" 
     aria-errormessage="error-diners" 
     aria-invalid="true"
     >
     <option value="">Diners</option>
     <option value="1">1</option>
     <option value="2">2</option>
     <option value="3">3</option>
     <option value="4">4</option>
     <option value="5">5</option>
     <option value="6">6</option>
     <option value="7">7</option>
     <option value="8">8</option>
  </Field>
     <ErrorMessage component="div" className="error--diners" name="NoOfDiners" id="error-diners" />

已尝试的方案

  • 在waitFor中添加async:
await waitFor(async() => {
     await expect(diners).toBeInTheDocument('* Please Select The Amount of Diners');
   });

或

await waitFor(async() => {
     await diners.toBeInTheDocument();
   });
  • 尝试解析expect:
expect(Promise.resolve(diners)).toBeInTheDocument();

此操作报错:cannot convert undefined or null to object

  • 不在waitFor中使用async,改用toHaveAccessibleErrorMessage:
await waitFor(() => {
    expect(diners).toHaveAccessibleErrorMessage('* Please Select The Amount of Diners');
  });

解决方案

1. 修正点击事件的异步处理

userEvent.click是异步方法,必须加await,否则可能在表单验证触发前就开始查找元素:

await user.click(screen.getByTestId('continue'))

2. 简化元素查找与断言逻辑

screen.findByText本身就是异步等待元素出现的方法,不需要额外嵌套waitFor。同时toBeInTheDocument不需要传文本参数,直接断言元素存在即可:

test('it shows 4 required fields', async () => {
    const user = userEvent.setup()
    
    await user.click(screen.getByTestId('continue'))
    
    const dinersError = await screen.findByText('* Please Select The Amount of Diners');
    expect(dinersError).toBeInTheDocument();
})

3. 匹配文本时注意大小写与精确性

检查错误提示的实际文本是否和测试中的完全一致(比如测试用的是The,而实际错误提示是否是the?大小写不匹配会导致查找失败)。如果不确定精确文本,可以用正则忽略大小写:

await screen.findByText(/Please Select The Amount of Diners/i)

4. 直接定位错误提示元素

由于ErrorMessage组件有id="error-diners",可以给它加上data-testid后直接定位,或者用角色查找:

<!-- 给ErrorMessage添加data-testid -->
<ErrorMessage component="div" className="error--diners" name="NoOfDiners" id="error-diners" data-testid="error-diners" />

测试代码中直接查找:

const dinersError = await screen.findByTestId('error-diners');
// 或者用角色查找
const dinersError = await screen.findByRole('alert', { name: /Please Select The Amount of Diners/i });

5. 确保Formik触发验证条件

Formik的错误提示需要字段被标记为touched才会显示,所以可以先触发字段的交互(比如点击下拉框)再点击按钮:

await user.click(screen.getByTestId('NoOfDiners'));
await user.click(screen.getByTestId('continue'));

内容的提问来源于stack exchange,提问作者Tom Gonzo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 22:57:37