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

Jest无法识别React组件中onClick修改的jurisdiction变量值

问题:React地址组件下拉选择未更新jurisdiction值,测试断言失败

我有一个Address React组件,其中包含由{streetAddress, additionalStreetAddress, city, jurisdiction, zipCode}定义的address对象。jurisdiction是一个包含美国所有州的下拉Select组件,代码如下:

<Select
  id={`${id}-jurisdiction`}
  name="jurisdiction"
  placeholderOption=" "
  labelVisual="State"
  value={jurisdiction}
>
  {JURISDICTIONS.map((currentJurisdiction) => (
    <SelectOption
      id={`jurisdiction-${currentJurisdiction}`}
      key={currentJurisdiction}
      value={currentJurisdiction}
      onClick={(e) => { jurisdiction = e.currentTarget.value; }}
    >
      {currentJurisdiction}
    </SelectOption>
  ))}
</Select>

我编写了如下测试用例验证该功能,但Jest期望得到'MA',实际却接收到空字符串:

it('should update selected option and updated address jurisdiction when user makes a selection', async () => {
  const jurisdictionDropDown = screen.getByRole('combobox');
  const user = userEvent.setup();
  const selectedJurisdiction = 'MA';
  await user.selectOptions(jurisdictionDropDown, [selectedJurisdiction]);
  expect(address.jurisdiction).toEqual('MA');
});

问题原因与修复方案

1. 组件状态更新逻辑错误

你直接通过jurisdiction = e.currentTarget.value修改变量的方式不符合React的状态管理规则:

  • 如果jurisdiction是组件内部状态(比如用useState声明的),直接赋值不会触发组件重渲染,也不会更新状态的实际值,状态仍会保持初始的空字符串。
  • 下拉选择的交互逻辑不应该绑定在SelectOption的onClick事件上,因为用户可能通过键盘导航选择选项,onClick无法覆盖所有交互场景,正确的做法是给Select组件绑定onChange事件(具体事件名需参考你使用的自定义Select组件的API)。

修复组件代码(函数组件示例):

import { useState } from 'react';

function Address({ id }) {
  // 用useState管理address状态
  const [address, setAddress] = useState({
    streetAddress: '',
    additionalStreetAddress: '',
    city: '',
    jurisdiction: '',
    zipCode: ''
  });

  return (
    <Select
      id={`${id}-jurisdiction`}
      name="jurisdiction"
      placeholderOption=" "
      labelVisual="State"
      value={address.jurisdiction}
      // 绑定Select的onChange事件更新状态
      onChange={(e) => {
        setAddress(prevAddress => ({
          ...prevAddress,
          jurisdiction: e.target.value
        }));
      }}
    >
      {JURISDICTIONS.map((currentJurisdiction) => (
        <SelectOption
          id={`jurisdiction-${currentJurisdiction}`}
          key={currentJurisdiction}
          value={currentJurisdiction}
        >
          {currentJurisdiction}
        </SelectOption>
      ))}
    </Select>
  );
}

2. 测试断言逻辑错误

如果address是组件内部的状态,测试代码无法直接访问到这个内部变量,你直接断言address.jurisdiction拿到的只是初始值的引用,不会同步组件内部更新后的状态。

修复测试代码的两种方式:

方式一:断言DOM元素的选中值

直接查询下拉框的当前value来验证选择是否生效:

it('should update selected option and updated address jurisdiction when user makes a selection', async () => {
  render(<Address id="test-address" />);
  const jurisdictionDropDown = screen.getByRole('combobox');
  const user = userEvent.setup();
  const selectedJurisdiction = 'MA';
  
  await user.selectOptions(jurisdictionDropDown, [selectedJurisdiction]);
  
  // 断言下拉框的当前值为MA
  expect(jurisdictionDropDown).toHaveValue(selectedJurisdiction);
});

方式二:通过回调函数验证状态更新

如果你的Address组件是受控组件(通过props接收address和状态更新回调),可以模拟回调函数来验证:

it('should update selected option and updated address jurisdiction when user makes a selection', async () => {
  const mockOnAddressChange = jest.fn();
  const initialAddress = {
    streetAddress: '',
    additionalStreetAddress: '',
    city: '',
    jurisdiction: '',
    zipCode: ''
  };
  
  render(<Address 
    id="test-address" 
    address={initialAddress} 
    onAddressChange={mockOnAddressChange} 
  />);
  
  const jurisdictionDropDown = screen.getByRole('combobox');
  const user = userEvent.setup();
  const selectedJurisdiction = 'MA';
  
  await user.selectOptions(jurisdictionDropDown, [selectedJurisdiction]);
  
  // 断言回调函数被调用,且参数包含正确的jurisdiction值
  expect(mockOnAddressChange).toHaveBeenCalledWith(expect.objectContaining({
    jurisdiction: selectedJurisdiction
  }));
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 00:09:24