HTML本身无组件概念,真正测试的是HTML+CSS+JS构成的有行为UI单元;必须用getByRole等语义化定位验证真实交互、状态同步与可访问性,而非仅测静态结构。

HTML 本身不能做组件级单元测试——你真正要测的是由 HTML + CSS + JS 构成的、有行为的 UI 单元,比如一个带点击反馈的 button、一个动态展开的 accordion,或封装了渲染逻辑的自定义元素。直接对静态 HTML 字符串写断言,既不覆盖交互,也不验证状态同步,等于没测。
为什么用 getByRole 定位比 querySelector 更可靠
自动化工具(如 Playwright 或 Testing Library)靠语义而非样式定位,才能跨实现方式保持稳定。比如一个按钮可能用 <button>、<div role="button"> 或 <a href="#"> 实现,只要它在可访问性树里是 button 类型且 name 正确,getByRole('button', { name: '删除' }) 就能命中。
- 如果没设
aria-label或name属性,getByRole会找不到——这不是 API 问题,是组件本身可访问性缺失 -
querySelector('.delete-btn')一旦 class 名重构就全挂,而语义定位只关心“用户看到什么、怎么操作” - React/Vue 组件测试中,
screen.getByRole还自动等待元素挂载,避免act warning或异步 race condition
禁用态、焦点、键盘导航必须真测,不能只看属性
很多组件用 class="disabled" 模拟禁用,或靠 CSS 隐藏但 DOM 仍存在;disabled 属性也可能被 JS 动态移除。仅检查 element.hasAttribute('disabled') 会漏掉真实用户行为。
- 用
await expect(button).toBeDisabled()(Playwright)或expect(button).toBeDisabled()(Testing Library),它们实际触发浏览器原生行为判断 - Tab 导航测试必须手动或用 Playwright 的
press('Tab')+focus()链式调用,验证焦点是否落到预期元素、:focus-visible样式是否生效 - 禁用 CSS 后(DevTools → ⌘+Shift+P → “Disable styles”),检查结构是否仍语义清晰——比如
<h2>不能靠字号撑起层级
jsdom 无法替代真实浏览器环境
用 Jest + jsdom 测 HTML 字符串生成函数(如 generateCardHtml(data))没问题,但一旦涉及 layout、focus、IntersectionObserver、图片加载或事件冒泡,jsdom 就会给出错误结论。
立即学习“前端免费学习笔记(深入)”;
-
element.offsetHeight在 jsdom 中恒为 0,哪怕你写了height: 100px -
img.naturalWidth始终是 0,无法验证图片是否成功加载 -
focus()不触发:focus样式,也无法测试键盘 tab 顺序是否合理 - 适合场景:纯数据 → HTML 字符串的映射逻辑(如模板函数),不适合测“用户点一下后页面怎么变”
真正难的不是写断言,而是定义清楚这个“组件”在用户眼里是什么行为——它有没有 keyboard focus?是否响应屏幕阅读器?禁用时是否阻止 click 和 keydown?这些细节不提前约定,测试就只是在测自己写的 DOM 结构,而不是测它能不能用。



















