如何使用Testing-Library、Jest和JSDOM测试自定义元素/Web Components?Angular项目自定义元素测试疑难咨询
我之前也碰到过类似的问题,结合你的场景来说,这不是你遗漏了配置,而是当前Jest+Testing Library的JSDOM环境对Stencil构建的自定义元素存在天然的支持限制,下面给你拆解原因和可行的解决思路:
为什么自定义元素的内部内容没渲染?
Jest默认使用的JSDOM环境,对自定义元素的生命周期处理和真实浏览器有差异:
- Stencil生成的自定义元素会在
connectedCallback中异步渲染内部DOM,但JSDOM不会像真实浏览器那样完整触发这些异步初始化逻辑,导致元素标签渲染了,但内部的<button>等内容没来得及生成。 - Testing Library的
render()方法是同步执行的,不会等待自定义元素的异步渲染完成,所以你打印页面时看不到内部内容,断言自然会失败。
可行的解决办法
1. 手动等待自定义元素完成初始化
Stencil构建的元素自带componentOnReady()方法(Stencil v2及以上版本),可以用它来等待元素完成渲染:
import { render, screen } from '@testing-library/angular'; import { AppComponent } from './app.component'; test('renders custom element button', async () => { const { container } = await render(AppComponent); // 获取自定义元素并等待它初始化完成 const customEl = container.querySelector('my-custom-element') as any; await customEl.componentOnReady(); // 现在可以正常断言内部元素了 expect(screen.getByRole('button')).toBeInTheDocument(); });
2. 完善Jest的自定义元素配置
确保你在Jest的全局setup文件中注册了所有自定义元素,比如导入Stencil库的loader:
// jest.setup.ts import { defineCustomElements } from '@your-stencil-library/loader'; // 注册所有自定义元素到全局window对象 defineCustomElements(window);
如果你的JSDOM版本较老(Jest 27配套的JSDOM版本确实不算新),可以添加@webcomponents/webcomponentsjs polyfill来补全自定义元素的生命周期支持:
npm install @webcomponents/webcomponentsjs --save-dev
然后在setup文件中导入:
import '@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js';
3. 改用真实浏览器测试工具
如果上述JSDOM环境的方案都不稳定,改用Cypress、Playwright这类真实浏览器测试工具是最稳妥的选择——它们完全模拟真实浏览器环境,自定义元素的渲染和线上表现一致,同时也能配合@testing-library/cypress这类适配库保持你的测试风格。
关于你提到的PR和文章
你说的那篇关于Shadow DOM支持的PR,针对的是带Shadow DOM的自定义元素场景,而你的元素没有使用Shadow DOM,所以即使合并也解决不了当前问题。那篇文章提到的"无法测试CE",特指JSDOM环境下的限制,并非所有测试场景都无法实现。
另外要说明的是:你的测试逻辑完全没问题,确实不是在测试实现细节——验证文本是否渲染是很合理的用户行为测试,只是工具链环境限制导致暂时不能直接运行。
内容的提问来源于stack exchange,提问作者felix

