.card__header--large比div > h2:first-child更适合测试,因其显式声明组件边界与状态,不依赖dom结构变动、标签类型或元素顺序,且支持精准状态断言与高效代码定位。

直接用 BEM 类名写测试选择器,能大幅降低测试脆弱性——因为类名本身已携带组件边界和状态语义,不用再靠 DOM 路径或模糊标签猜结构。
为什么 .card__header--large 比 div > h2:first-child 更适合写测试
BEM 类名是显式契约:它声明了“这是 card 组件的 header 元素,且当前处于 large 状态”。测试时直接选 .card__header--large,就锁定了目标节点的归属与状态,不依赖父级是否换了容器、子元素是否增删顺序、甚至是否用了 flex 替代 block 布局。
- DOM 结构变动(比如把
<h2></h2>换成<div role="heading">)不影响测试,只要类名不变 <li>JS 动态切换状态(如点击后加 <code>.card--expanded)可被精准断言,无需模拟事件链推导最终样式 - 回归测试时,搜索
card__header就能定位所有相关 HTML 和 CSS,不会漏掉隐藏在.sidebar .card h2里的老写法 - 只对 block 或 element 级类名做断言,例如
.search-form__input✅,但避免断言.search-form__input--disabled单独存在(它必须和.search-form__input同时出现) - Modifier 必须成对验证:断言
.button--loading时,同步检查.button是否仍在 classList 中,否则说明 JS 错误覆盖了基础类 - 禁止跨 block 选择:
.user-card__avatar可以测,但.user-card__avatar__icon是非法命名,测试发现它说明代码已违反 BEM,该先修复再测 - 如果组件已严格遵循 BEM,且类名无动态拼接(比如没写
className={`card__body ${size === 'large' ? 'card__body--large' : ''}`}),直接用getByClassName('card__body')更轻量、更直观 - 若存在运行时类名拼接,或团队明确禁止依赖 class 名(如担心设计师改名),那就用
data-testid="card-body",但命名仍按 BEM 逻辑:testid 值应为block__element格式,保持语义一致 - 绝对不要混用:同一组件内既写
data-testid="header"又保留.card__header,会导致测试路径分裂,后续有人删 class 却忘了删 testid
测试中怎么安全地用 __ 和 -- 选择器
关键不是“能不能用”,而是“用在哪一层”。BEM 的层级约束在测试里同样生效:
遇到 getByTestId 和 getByClassName 冲突怎么办
React 测试库推荐 getByTestId 是为解耦渲染逻辑,但 BEM 类名天然比随机 data-testid 更稳定。实际取舍看维护成本:
BEM 对测试真正的价值不在“多写了几个下划线”,而在于把组件的边界、状态、嵌套关系提前固化在类名里——测试时你不是在猜结构,是在验证契约是否被遵守。最容易被忽略的是:Modifier 类名一旦出现在 HTML 中,就必须有对应 CSS 规则,否则测试通过但视觉失效,这种“假阳性”得靠构建时 lint 工具(如 postcss-bem-linter)卡住,不能只靠测试覆盖。











