Jest测试React MUI表格分页:切换每页行数弹窗不显示问题
解决Jest测试MUI表格分页行数选择弹窗不显示的问题
我在Next.js项目里用了带分页的MUI数据表格,在Jest测试切换每页行数(从5改到10)时,点击切换按钮后,行数选择弹窗死活不出来。
问题细节
表格页脚的渲染结构如下:
<div class="MuiTablePagination-root css-jtlhu6-MuiTablePagination-root" data-testid="tablePagination" style="display: block;"> <div class="MuiToolbar-root MuiToolbar-gutters MuiToolbar-regular MuiTablePagination-toolbar css-78c6dr-MuiToolbar-root-MuiTablePagination-toolbar"> <div class="MuiTablePagination-spacer css-1psng7p-MuiTablePagination-spacer"></div> <p class="MuiTablePagination-selectLabel css-pdct74-MuiTablePagination-selectLabel" id=":r7:">Rows per page:</p> <div class="MuiInputBase-root MuiInputBase-colorPrimary Mui-focused css-16c50h-MuiInputBase-root-MuiTablePagination-select"> <div tabindex="0" role="button" aria-expanded="true" aria-haspopup="listbox" aria-labelledby=":r7: :r6:" id=":r6:" class="MuiSelect-select MuiTablePagination-select MuiSelect-standard MuiInputBase-input css-194a1fa-MuiSelect-select-MuiInputBase-input">5</div> <input aria-hidden="true" tabindex="-1" class="MuiSelect-nativeInput css-yf8vq0-MuiSelect-nativeInput" value="5"><svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium MuiSelect-icon MuiTablePagination-selectIcon MuiSelect-iconStandard MuiSelect-iconOpen css-1mf6u8l-MuiSvgIcon-root-MuiSelect-icon" focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="ArrowDropDownIcon"> <path d="M7 10l5 5 5-5z"></path></svg></div><p class="MuiTablePagination-displayedRows css-levciy-MuiTablePagination-displayedRows">1–5 of 6</p><div class="MuiTablePagination-actions"> <button class="MuiButtonBase-root Mui-disabled MuiIconButton-root Mui-disabled MuiIconButton-colorInherit MuiIconButton-sizeMedium css-zylse7-MuiButtonBase-root-MuiIconButton-root" tabindex="-1" type="button" disabled="" aria-label="Go to previous page" title="Go to previous page"> <svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-i4bv87-MuiSvgIcon-root" focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="KeyboardArrowLeftIcon"><path d="M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z"></path></svg> </button> <button class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit MuiIconButton-sizeMedium css-zylse7-MuiButtonBase-root-MuiIconButton-root" tabindex="0" type="button" aria-label="Go to next page" title="Go to next page"> <svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-i4bv87-MuiSvgIcon-root" focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="KeyboardArrowRightIcon"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg> <span class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"></span> </button> </div> </div> </div>
测试时能定位到页面中role="button"且tabindex=0的div,也能触发fireEvent.click事件,但DOM里就是不生成可选行数的菜单。正常情况下,点击后body末尾会动态添加以下菜单结构:
<div role="presentation" class="MuiPopover-root MuiMenu-root MuiModal-root css-10nakn3-MuiModal-root-MuiPopover-root-MuiMenu-root" id="menu-"> <div aria-hidden="true" class="MuiBackdrop-root MuiBackdrop-invisible css-g3hgs1-MuiBackdrop-root-MuiModal-backdrop" style="opacity: 1; transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;"></div> <div tabindex="0" data-testid="sentinelStart"></div> <div class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 MuiPaper-root MuiMenu-paper MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation8 MuiPopover-paper css-1poimk-MuiPaper-root-MuiMenu-paper-MuiPaper-root-MuiPopover-paper" tabindex="-1" style="opacity: 1; transform: none; min-width: 48px; transition: opacity 242ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, transform 161ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; top: 600px; left: 452px; transform-origin: 24px 53.6625px;"> <ul class="MuiList-root MuiList-padding MuiMenu-list css-6hp17o-MuiList-root-MuiMenu-list" role="listbox" tabindex="-1" aria-labelledby=":r7:"> <li class="MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters Mui-selected MuiTablePagination-menuItem css-10by3a5-MuiButtonBase-root-MuiMenuItem-root-MuiTablePagination-menuItem" tabindex="0" role="option" aria-selected="true" data-value="5">5<span class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"></span></li> <li class="MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters MuiTablePagination-menuItem css-10by3a5-MuiButtonBase-root-MuiMenuItem-root-MuiTablePagination-menuItem" tabindex="-1" role="option" aria-selected="false" data-value="10">10<span class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"></span></li> <li class="MuiButtonBase-root MuiMenuItem-root MuiMenuItem-gutters MuiTablePagination-menuItem css-10by3a5-MuiButtonBase-root-MuiMenuItem-root-MuiTablePagination-menuItem" tabindex="-1" role="option" aria-selected="false" data-value="25">25<span class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"></span></li> </ul> </div> <div tabindex="0" data-testid="sentinelEnd"></div> </div>
另外,浏览器中点击显示“5”的button div时,aria-expanded会从false变为true,但Jest里这个属性始终保持false。核心问题是MUI表格此处没有用常规select组件,而是通过点击按钮触发弹窗菜单,直接触发click事件无法触发组件的完整交互逻辑。
解决方案:正确测试MUI Select组件的步骤
MUI的Select组件(包括表格分页的行数选择器)基于Popover/Menu实现,测试时要模拟真实用户的交互流程:
1. 用userEvent触发选择器点击
不要用fireEvent.click,改用userEvent.click,它会模拟完整的用户交互链(包括焦点切换、事件冒泡等):
import { screen, userEvent } from '@testing-library/react'; // 定位行数选择器按钮 const rowsPerPageButton = screen.getByRole('button', { name: /rows per page/i }); // 触发点击 await userEvent.click(rowsPerPageButton);
2. 等待菜单渲染并选择目标选项
菜单是动态挂载到body的,需要用异步查询方法等待它出现,再选择目标选项:
// 等待菜单中“10”选项加载完成 const tenRowsOption = await screen.findByRole('option', { name: '10' }); // 点击选择该选项 await userEvent.click(tenRowsOption);
3. 验证切换结果
最后验证行数选择器的显示内容或表格的行数范围是否更新:
// 验证行数选择器显示“10” expect(screen.getByRole('button', { name: /rows per page/i })).toHaveTextContent('10'); // 验证表格显示的行数范围 expect(screen.getByText('1–10 of 6')).toBeInTheDocument();
关键说明
- 必须使用
userEvent:MUI组件依赖完整的用户交互事件链,fireEvent仅触发单一事件,无法模拟真实点击流程。 - 用异步查询方法:菜单动态挂载到body,
findByRole等异步方法会等待组件渲染完成。 - 基于语义化属性定位:避免依赖CSS类名或ID,用
role、name等语义化属性定位元素,提升测试健壮性。
内容的提问来源于stack exchange,提问作者DaReign
相关产品推荐
相关产品推荐

