如何用@testing-library/react更新组件Props?Jest测试新手求教
如何在Jest + @testing-library中测试组件Props更新
先指出你现有测试代码的几个问题:
- 测试顺序搞反了:还没渲染组件就去查询
test-dropdown元素,肯定会报错,得先渲染再做断言。 onSelect绑定错误:你写的onSelect={onSelectMock(items[1])}是直接执行了mock函数,不是传递点击时要触发的回调,应该改成传递一个函数。- 未正确获取点击元素:
el[1]根本没定义,得用测试库的查询方法(比如getAllByRole)拿到对应的选项元素。 - 受控组件逻辑没处理:你的Dropdown是受控组件,
selected值完全由外部props决定,点击选项后需要父组件主动更新selected并重新传递给Dropdown,组件才会显示新值,原测试没做这一步。
修正后的测试方案(受控组件场景)
如果要保持Dropdown的受控属性,测试时需要模拟父组件更新props的行为,用测试库的rerender方法重新渲染组件:
import { render, screen, fireEvent } from '@testing-library/react'; import Dropdown from './Dropdown'; const items = [{id: 1, name: "1"}, {id: 2, name: "2"}]; const initialValue = "Select"; test("点击选项后更新Dropdown的selected值", () => { const onSelectMock = jest.fn(); let currentSelected = undefined; // 初始渲染组件,传入初始状态的selected const { rerender } = render( <Dropdown items={items} initialValue={initialValue} selected={currentSelected} onSelect={() => { // 模拟父组件接收到选择事件后更新selected currentSelected = items[1].name; // 重新渲染组件,传入更新后的selected rerender( <Dropdown items={items} initialValue={initialValue} selected={currentSelected} onSelect={onSelectMock} /> ); }} /> ); // 验证初始显示文本 expect(screen.getByTestId('test-dropdown')).toHaveTextContent('Select'); // 获取第二个选项并点击 const secondOption = screen.getAllByRole('list')[1]; fireEvent.click(secondOption); // 验证显示文本已更新 expect(screen.getByTestId('test-dropdown')).toHaveTextContent('2'); // 验证onSelect回调被触发 expect(onSelectMock).toHaveBeenCalled(); });
更贴近实际场景的改进:改成非受控组件
如果希望Dropdown自己维护选中状态(不需要父组件每次手动更新),可以把组件改成非受控模式,内部用useState管理选中值,这样测试会更简单:
改造后的Dropdown组件:
import { useState } from 'react'; interface IDropdownProps { initialValue: string; items: Array<{ id: number; name: string }>; selected?: string; onSelect?: (item: { id: number; name: string }) => void; } const Dropdown: React.FC<IDropdownProps> = ({ initialValue, items, selected, onSelect }) => { // 内部维护选中状态,优先使用外部传入的selected(兼容受控模式) const [internalSelected, setInternalSelected] = useState(selected || initialValue); const handleClick = (item) => { setInternalSelected(item.name); // 触发外部回调通知选择结果 onSelect?.(item); }; return ( <div> <div data-testid="test-dropdown">{internalSelected}</div> <ul> {items.map((el) => ( <li key={`a${el.id}`} onClick={() => handleClick(el)} role="listitem" // 注意:li的role应该是listitem,ul才是list > {el.name} </li> ))} </ul> </div> ); }; export default Dropdown;
对应的测试代码:
import { render, screen, fireEvent } from '@testing-library/react'; import Dropdown from './Dropdown'; const items = [{id: 1, name: "1"}, {id: 2, name: "2"}]; const initialValue = "Select"; test("点击选项后自动更新显示文本", () => { const onSelectMock = jest.fn(); render(<Dropdown items={items} initialValue={initialValue} onSelect={onSelectMock} />); // 初始状态验证 expect(screen.getByTestId('test-dropdown')).toHaveTextContent('Select'); // 点击第二个选项 const secondOption = screen.getAllByRole('listitem')[1]; fireEvent.click(secondOption); // 验证显示文本更新 expect(screen.getByTestId('test-dropdown')).toHaveTextContent('2'); // 验证回调函数拿到了选中的项 expect(onSelectMock).toHaveBeenCalledWith(items[1]); });
内容的提问来源于stack exchange,提问作者Humanoid Mk.12
相关产品推荐
相关产品推荐

