问询enzyme、react-dom/test-tools或jsdom是否存在clientHeight相关已知缺陷
Enzyme/react-dom/test-tools/jsdom中clientHeight返回undefined的已知问题及解决办法
首先明确回答你的问题:是的,这类问题确实存在,核心原因在于jsdom(Enzyme的mount方法依赖它模拟DOM环境)并没有实现完整的浏览器布局渲染逻辑——它只会构建DOM树结构,但不会像真实浏览器那样计算元素的实际尺寸,这就导致clientHeight、offsetHeight这类需要布局计算的属性在测试环境中经常返回undefined或不正确的值。
为什么你的代码在浏览器里正常,测试里不行?
在真实浏览器中,组件挂载后浏览器会执行布局渲染,计算出元素的实际高度,clientHeight能拿到正确值;但在jsdom环境中,没有这个布局计算的过程,元素的尺寸属性默认不会被赋值,所以你的this.content.firstChild.clientHeight就成了undefined。
具体的解决方案
针对你的场景,这里有几种可行的处理方式:
1. 测试中手动模拟clientHeight的值
最简单的方式是在测试流程中,直接给目标DOM元素的clientHeight赋值,模拟真实浏览器的计算结果:
// Arrange const wrapper = mount( <MyWidget trigger={<span>click me</span>} open={false}> <div style={{ height: 90, padding: 5 }}>Lorum Ipsum</div> </MyWidget> ); // 找到内容容器的DOM实例,手动设置子元素的clientHeight const contentDom = wrapper.find('#your-control-id').instance(); contentDom.firstChild.clientHeight = 100; // 对应你期望的90+5+5=100 // Act wrapper.setProps({ open: true }); // Assert expect(wrapper.children('div').prop('style')).to.have.property('maxHeight', 100);
2. 用jest spy模拟HTMLElement的clientHeight getter
如果不想在测试中逐一查找元素,也可以全局模拟clientHeight的获取逻辑:
// 在测试开始前模拟clientHeight beforeEach(() => { jest.spyOn(HTMLElement.prototype, 'clientHeight', 'get').mockReturnValue(100); }); // 测试结束后恢复原实现 afterEach(() => { jest.restoreAllMocks(); }); // 你的测试代码 test('should set correct maxHeight when open', () => { const wrapper = mount( <MyWidget trigger={<span>click me</span>} open={false}> <div style={{ height: 90, padding: 5 }}>Lorum Ipsum</div> </MyWidget> ); wrapper.setProps({ open: true }); expect(wrapper.children('div').prop('style')).to.have.property('maxHeight', 100); });
3. 调整组件逻辑,减少对DOM尺寸的直接依赖
从长远来看,你可以重构组件逻辑,避免直接读取clientHeight:
- 比如将子元素的高度通过props传递给
MyWidget,这样测试时可以直接传入预期值; - 或者使用CSS的
max-height过渡方案,结合overflow: hidden,不需要通过JS计算高度(比如设置max-height: 0和max-height: 1000px的过渡,来模拟展开收起效果)。
补充说明
这个问题并非Enzyme的缺陷,而是jsdom的设计限制——它的定位是模拟DOM API,而非实现完整的浏览器渲染引擎。react-dom/test-tools也依赖jsdom,所以同样会遇到这个问题。
内容的提问来源于stack exchange,提问作者Michael Papworth
相关产品推荐
相关产品推荐

