HTML模板测试需显式构造Shadow DOM、清理副作用、模拟依赖三者配合,否则因light DOM无边界、状态残留、全局污染导致测试相互影响和断言失效。

HTML模板在前端测试中无法天然隔离,必须靠显式构造 Shadow DOM + 清理副作用 + 模拟依赖三者配合,否则测试会相互污染、状态残留、断言失效。
为什么<template>直接克隆进测试 DOM 会失败
很多人把子组件 HTML 写进<template id="card">,然后在 Jest 或 Vitest 中用document.body.appendChild(tmpl.content.cloneNode(true))渲染,结果测试间互相影响:前一个测试改了localStorage,后一个就拿到脏数据;前一个绑了window.addEventListener('resize'),后一个fireEvent触发两次。
-
<template>只是静态占位符,克隆后所有节点都落在 light DOM,样式、事件、全局状态全无边界 -
cloneNode(true)丢掉<input checked>状态、<select>选中项,导致 UI 断言失真 - 没清理
setTimeout、fetchmock、localStorage写入,下一个测试用的是上个测试的残留
测试中用attachShadow({ mode: 'closed' })的实操要点
真正隔离的起点不是模板,而是自定义元素实例本身——必须让它在测试环境里也走完整生命周期。
- 测试前手动创建实例:
const el = document.createElement('my-card');,不能只塞 HTML 字符串 - 在
beforeEach里调用el.attachShadow({ mode: 'closed' }),别等connectedCallback——否则样式已注入 light DOM - 用
el.shadowRoot.appendChild(fragment)挂载模板内容,而不是document.body.appendChild - 验证时必须用
el.shadowRoot.querySelector('.my-card__title'),document.querySelector查不到内部节点
如何安全注入<style>和<script>到测试用 Shadow DOM
测试里不能容忍 XSS 或全局污染,innerHTML = '<style>...</style>'是红线。
立即学习“前端免费学习笔记(深入)”;
- 提取
<style>文本,用new CSSStyleSheet()加载,再赋给el.shadowRoot.adoptedStyleSheets = [sheet](注意兼容性:Chrome 73+ / Safari 16.4+) - 剥离所有
<script>标签,用jest.mock或vi.mock拦截模块导入,禁止执行真实脚本 - 若需模拟脚本副作用(如初始化第三方 SDK),用
Object.defineProperty(window, 'SDK', { value: mockSDK })替代直接插入<script> - 每次测试后重置
localStorage、清除setTimeout、还原fetchmock——工具链不自动做这事
组件测试中容易被忽略的隔离点
最常漏掉的不是 Shadow DOM,而是那些“看起来跟组件无关”的全局钩子。
-
document.title被改过一次,后续测试读到的是旧值,得在afterEach里设回空字符串 - 多个测试共用同一个
CustomEvent('submit'),监听器没移除就会累积触发 - 组件内用了
IntersectionObserver,测试跑完没disconnect(),下次测试可能收到上一轮回调 - 用
requestAnimationFrame做动画,测试结束时帧还没执行,得用jest.runAllTimers()强制清空



















