Angular 5指令单元测试中document.write创建元素offsetHeight为0的问题
解决Angular测试中Mock Document元素offsetHeight始终为0的问题
这个问题的核心原因很明确:你通过document.implementation.createHTMLDocument创建的是一个脱离当前页面DOM树的孤立文档,里面的元素从来没有被浏览器渲染过——而offsetHeight是依赖浏览器布局渲染才能计算出的属性,没经过渲染的元素自然会返回0,哪怕你设置了style.height也没用。
下面给你两种可行的解决方案,你可以根据测试场景选择:
方案1:将Mock元素挂载到真实测试DOM中
把创建的Mock元素添加到测试环境的真实DOM里(比如页面的<body>),让浏览器完成渲染计算,这样offsetHeight就能正常获取了:
class MockDocumentService { querySelectorAll(selector: string) { const doc = document.implementation.createHTMLDocument(''); doc.open(); doc.write(`<html><body><div> <div MaxHeight="testMaxHeight">some text</div> <div style="height:100px" MaxHeight="testMaxHeight">a taller div</div> <div MaxHeight="testMaxHeight">some text</div> </div></body></html>`); doc.close(); // 将Mock的内容挂载到测试页面的body中 const mockContainer = doc.body.firstChild; document.body.appendChild(mockContainer); // 返回查询到的元素(此时它们已在DOM中,能被渲染) return doc.querySelectorAll(selector); } }
⚠️ 注意:测试结束后一定要清理这些Mock元素,避免污染其他测试用例:
afterEach(() => { // 移除所有测试用的Mock元素 const testElements = document.querySelectorAll('[MaxHeight="testMaxHeight"]'); testElements.forEach(el => el.remove()); // 也可以直接移除挂载的容器 const mockContainer = document.body.querySelector('div:first-child'); if (mockContainer) mockContainer.remove(); });
方案2:手动Mock offsetHeight属性(更适合单元测试)
如果你不想依赖浏览器渲染,也可以直接给Mock元素手动定义offsetHeight的值——因为offsetHeight是只读属性,需要用Object.defineProperty来重写:
class MockDocumentService { querySelectorAll(selector: string) { const doc = document.implementation.createHTMLDocument(''); doc.open(); doc.write(`<html><body><div> <div MaxHeight="testMaxHeight">some text</div> <div style="height:100px" MaxHeight="testMaxHeight">a taller div</div> <div MaxHeight="testMaxHeight">some text</div> </div></body></html>`); doc.close(); const elements = doc.querySelectorAll(selector); elements.forEach(el => { const htmlEl = el as HTMLElement; // 根据元素的style或其他特征,手动设置offsetHeight if (htmlEl.style.height === '100px') { Object.defineProperty(htmlEl, 'offsetHeight', { value: 100, writable: false // 保持只读特性,和真实属性一致 }); } else { // 普通文本div的默认高度,你可以根据实际情况调整 Object.defineProperty(htmlEl, 'offsetHeight', { value: 20 }); } }); return elements; } }
这种方法的优势是完全可控,不需要依赖浏览器的渲染逻辑,单元测试的执行速度也更快。
为什么实际使用中没问题?
因为在生产环境里,你的指令操作的是真实DOM树中的元素,浏览器已经完成了布局渲染,offsetHeight是经过计算后的真实值,自然能正常获取。
内容的提问来源于stack exchange,提问作者HisDivineShadow
相关产品推荐
相关产品推荐

