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

如何用react-testing-library模拟单元格内容溢出以测试Popover?

问题分析:确实是虚拟渲染(JSDOM)的锅

没错,你遇到的问题完全是Jest默认使用的JSDOM虚拟渲染环境导致的。JSDOM只是模拟了DOM的结构和部分API,但它没有真实浏览器的布局引擎——也就是说,它不会计算元素的实际渲染宽度,也不会处理CSS的max-width、white-space: nowrap这些样式带来的溢出逻辑。不管你内容多长,JSDOM都会让scrollWidth和clientWidth保持相等,自然触发不了你写的溢出判断。

解决办法:强制触发溢出状态的几种方式

当然可以强制触发溢出行为,这里给你几个实用的方案,按推荐程度排序:

1. 抽离溢出判断逻辑,测试时手动注入

这是最优雅的做法,把判断元素是否溢出的逻辑单独抽成一个函数,组件依赖这个函数而不是直接操作DOM属性。这样测试时你可以直接mock这个函数返回true,跳过DOM布局的依赖。

比如你的组件逻辑可以这样调整:

// OverflowCell.jsx
import { useState, useRef } from 'react';
import { Typography, Popover } from '@mui/material';
import { isOverflowing } from './utils';

export const OverflowCell = ({ content }) => {
  const [anchorEl, setAnchorEl] = useState(null);
  const typographyRef = useRef(null);

  const handleMouseEnter = () => {
    if (isOverflowing(typographyRef.current)) {
      setAnchorEl(typographyRef.current);
    }
  };

  const handleMouseLeave = () => {
    setAnchorEl(null);
  };

  return (
    <>
      <Typography
        ref={typographyRef}
        noWrap
        sx={{ maxWidth: '50px' }}
        onMouseEnter={handleMouseEnter}
        onMouseLeave={handleMouseLeave}
        data-testid="overflow-cell-typography"
      >
        {content}
      </Typography>
      <Popover open={!!anchorEl} anchorEl={anchorEl}>
        <Typography>{content}</Typography>
      </Popover>
    </>
  );
};

对应的溢出判断函数:

// utils.js
export const isOverflowing = (element) => {
  if (!element) return false;
  return element.scrollWidth > element.clientWidth;
};

测试时直接mock这个函数:

// OverflowCell.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import { OverflowCell } from './OverflowCell';
import { isOverflowing } from './utils';

// Mock溢出判断函数
jest.mock('./utils', () => ({
  ...jest.requireActual('./utils'),
  isOverflowing: jest.fn(),
}));

test('popover shows on hover when cell overflows', () => {
  // 强制让判断函数返回true,模拟溢出状态
  isOverflowing.mockReturnValue(true);

  const longContent = '这是一段超级长的测试内容,用来触发单元格溢出显示省略号';
  render(<OverflowCell content={longContent} />);

  // 找到单元格元素并触发hover
  const cell = screen.getByTestId('overflow-cell-typography');
  fireEvent.mouseEnter(cell);

  // 验证Popover里的完整内容存在
  expect(screen.getByText(longContent)).toBeInTheDocument();
});

2. 手动修改DOM元素的scrollWidth/clientWidth属性

如果不想重构代码,也可以直接在测试中手动篡改DOM元素的属性,让scrollWidth大于clientWidth,从而触发溢出判断。

示例代码:

test('popover shows on hover when cell overflows', () => {
  const longContent = '这是一段超级长的测试内容,用来触发单元格溢出显示省略号';
  render(<OverflowCell content={longContent} />);

  // 获取Typography元素
  const typographyEl = screen.getByTestId('overflow-cell-typography');

  // 手动设置scrollWidth和clientWidth,模拟溢出
  Object.defineProperty(typographyEl, 'scrollWidth', { value: 200 });
  Object.defineProperty(typographyEl, 'clientWidth', { value: 50 });

  // 触发hover事件
  fireEvent.mouseEnter(typographyEl);

  // 验证Popover显示完整内容
  expect(screen.getByText(longContent)).toBeInTheDocument();
});

注意:这种方式依赖DOM元素的引用,建议给目标元素加data-testid方便准确查找。

3. 使用支持布局的测试环境(可选)

如果你的项目需要大量测试CSS布局相关的逻辑,可以考虑使用端到端测试工具(比如Playwright、Cypress),它们会启动真实浏览器,自然能处理布局和溢出逻辑。不过这种方式成本较高,适合复杂场景。

总结

最推荐第一种方案,把判断逻辑抽离后,测试不仅更简单,也更聚焦于组件的行为而非DOM的细节。如果不想重构,第二种手动修改属性的方式也能快速解决问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 11:02:35