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

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",

问题根源

  1. 重复选项的DOM不匹配:页面存在两个name="French"的<option>元素(分属不同<optgroup>),且它们的value完全相同。你选中的是第一个,但React状态更新后,实际生效的可能是第二个选项,而你断言的始终是第一个的selected属性,导致结果为false。
  2. 异步状态更新未等待:userEvent.selectOptions触发的组件状态更新是异步的,直接断言会捕获更新前的DOM状态。
  3. 第三方组件的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 09:05:36