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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 15:10:03