React Testing Library+Jest测试Shadow DOM自定义表单元素遇阻
问题排查与解决方案
核心问题分析
- 循环逻辑错误:你每个
hds-input组件的Shadow DOM里只对应单个表单字段(比如mapName对应一个hds-input,rawDataSiteName对应另一个),但你在循环所有hds-input时,每个元素里都去查找两个不同name的输入框,必然会有一个返回null——因为当前遍历的hds-input根本不包含另一个字段。 - Shadow DOM访问方式问题:Testing Library默认不自动穿透Shadow DOM,但可以通过手动访问元素的
shadowRoot属性来获取内部元素,同时注意异步操作的处理。
修正后的测试代码示例
首先给React组件中的每个hds-input添加data-testid标识(方便精准定位):
// 在你的Order3DMaps组件中 <hds-input data-testid="map-name-input" name="mapName" /> <hds-input data-testid="raw-data-site-input" name="rawDataSiteName" /> <hds-input data-testid="organization-id-input" name="organizationId" disabled />
然后编写测试用例:
it("check form input fields with dummy values", async () => { render(<Order3DMaps />); expect(screen.getByText("Basic Information")).toBeInTheDocument(); // 精准定位每个hds-input组件 const mapNameHdsInput = screen.getByTestId("map-name-input"); const rawDataSiteHdsInput = screen.getByTestId("raw-data-site-input"); const organizationIdHdsInput = screen.getByTestId("organization-id-input"); // 从每个组件的Shadow DOM中获取对应的输入框 const mapNameField = mapNameHdsInput.shadowRoot.querySelector("input[name='mapName']"); const rawDataSiteField = rawDataSiteHdsInput.shadowRoot.querySelector("input[name='rawDataSiteName']"); const organizationIdField = organizationIdHdsInput.shadowRoot.querySelector("input[name='organizationId']"); // 异步操作要加await,保证输入完成后再进行断言 await userEvent.type(mapNameField, "testmapname"); await userEvent.type(rawDataSiteField, "testrawsite"); // 验证禁用状态 expect(organizationIdField).toBeDisabled(); });
进阶优化(适配新版Testing Library)
如果你的@testing-library/dom版本≥8.11.0,可以直接使用带shadow: true参数的查询方法,无需手动访问shadowRoot,更符合Testing Library的查询理念:
it("check form input fields with dummy values", async () => { render(<Order3DMaps />); expect(screen.getByText("Basic Information")).toBeInTheDocument(); // 直接穿透Shadow DOM查找输入框(按用户可见的标签/名称查询) const mapNameField = await screen.findByRole('textbox', { name: /map name/i, shadow: true }); const rawDataSiteField = await screen.findByRole('textbox', { name: /raw data site name/i, shadow: true }); const organizationIdField = await screen.findByRole('textbox', { name: /organization id/i, shadow: true }); await userEvent.type(mapNameField, "testmapname"); await userEvent.type(rawDataSiteField, "testrawsite"); expect(organizationIdField).toBeDisabled(); });
内容的提问来源于stack exchange,提问作者RAVI singh
相关产品推荐
相关产品推荐

