使用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
相关产品推荐
相关产品推荐

