Testing Library userEvent.selectOptions选项选中断言失败问题
问题分析与解决方案
问题背景
我有一个React组件,渲染了带选项的第三方combobox,部署后运行正常,对应的HTML结构如下:
<select class="country-select" id="nationalityCode" name="nationalityCode" required=""> <option value="" disabled="" hidden="">Choose</option> <optgroup label="Most used"> <option value="0052">Belgium</option> <option value="0055">German Citizien</option> <option value="0057">French</option> <option value="0001">Dutch</option> </optgroup> <optgroup label="Alle Nationalities"> <option value="0052">Belgium</option> <option value="0055">German Citizien</option> <option value="0057">French</option> <option value="0001">Dutch</option> <option value="0050">Albanese</option> <option value="0223">American Citizen</option> </optgroup> </select>
使用Testing Library的userEvent.selectOptions方法时,看似选项已选中,但断言失败,报错:
Error: expect(received).toBe(expected) // Object.is equality Expected: true Received: false
调试发现初始选中的选项仍处于选中状态,测试代码如下:
if (changeValue.nationaliteit) { const selectOption = screen.getAllByRole('option', { name: 'French' })[0]; await userEvent.selectOptions( screen.getByRole('combobox', { name: 'Nationaliteit' }), selectOption ); expect( (screen.getAllByRole('option', { name: 'French' })[0] as HTMLOptionElement).selected ).toBe(true); }
依赖版本:
"@testing-library/user-event": "^14.4.3", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^12.1.5",
问题根源
- 重复选项的DOM不匹配:页面存在两个
name="French"的<option>元素(分属不同<optgroup>),且它们的value完全相同。你选中的是第一个,但React状态更新后,实际生效的可能是第二个选项,而你断言的始终是第一个的selected属性,导致结果为false。 - 异步状态更新未等待:
userEvent.selectOptions触发的组件状态更新是异步的,直接断言会捕获更新前的DOM状态。 - 第三方组件的DOM同步延迟:部分第三方combobox组件可能不会立即同步更新DOM中option的
selected属性,而是通过内部状态管理选中状态。
解决办法
方案一:断言select元素的value(最推荐)
不要纠结单个option的selected属性,直接检查<select>的value是否等于目标值。这是更可靠的方式,因为无论哪个option被选中,select的value都会同步为对应值:
if (changeValue.nationaliteit) { await userEvent.selectOptions( screen.getByRole('combobox', { name: 'Nationaliteit' }), screen.getByRole('option', { name: 'French' }) ); const selectEl = screen.getByRole('combobox', { name: 'Nationaliteit' }) as HTMLSelectElement; expect(selectEl.value).toBe('0057'); }
方案二:等待DOM更新并精准定位目标option
如果必须断言option的selected属性,需要用waitFor等待状态更新,同时精准定位要选中的option(比如通过所属的optgroup):
import { waitFor } from '@testing-library/react'; if (changeValue.nationaliteit) { // 定位"Most used"分组下的French选项 const targetOption = screen.getByRole('option', { name: 'French', container: screen.getByLabelText('Most used') }); await userEvent.selectOptions( screen.getByRole('combobox', { name: 'Nationaliteit' }), targetOption ); // 等待状态同步后再断言 await waitFor(() => { expect((targetOption as HTMLOptionElement).selected).toBe(true); }); }
方案三:优化组件渲染,避免重复选项(从根源解决)
如果有权限修改组件代码,建议避免渲染value重复的选项,或者给重复选项添加唯一data-testid,方便测试时精准定位:
<!-- 修改后的组件HTML --> <optgroup label="Most used"> <option value="0057" data-testid="french-most-used">French</option> </optgroup> <optgroup label="Alle Nationalities"> <option value="0057" data-testid="french-all">French</option> </optgroup>
测试代码:
if (changeValue.nationaliteit) { const targetOption = screen.getByTestId('french-most-used'); await userEvent.selectOptions( screen.getByRole('combobox', { name: 'Nationaliteit' }), targetOption ); await waitFor(() => { expect((targetOption as HTMLOptionElement).selected).toBe(true); }); }
内容的提问来源于stack exchange,提问作者pcvnes
相关产品推荐
相关产品推荐

