Angular测试含Shadow DOM的按钮:无法访问<button>元素的问题
问题描述
我有一个简单组件,正尝试测试其中按钮的功能。该元素渲染后会生成包含<button>的Shadow DOM。在测试中,我能够获取到shadowRoot,但查询该shadowRoot时返回null。
测试代码如下:
describe('HeaderComponent', () => { let component: HeaderComponent; let fixture: ComponentFixture<HeaderComponent>; beforeEach(() => { TestBed.configureTestingModule({ declarations: [ HeaderComponent ], imports: [IonicModule.forRoot(), TransparentHeaderScrollModule,] }).compileComponents(); }); describe('with default props', () => { beforeEach(() => { fixture = TestBed.createComponent(HeaderComponent); component = fixture.componentInstance; fixture.detectChanges(); }) it('should emit when dismissed clicked', async () => { spyOn(component.handleDismissClick, 'emit') customElements.whenDefined('ion-button').then(() => { const headerElement = fixture.debugElement const ionButton = headerElement.query(By.css('.submitButton')).nativeElement as HTMLElement // displays the <ion-button> element console.log('ionButton', ionButton) const root = ionButton.shadowRoot // see image below console.log('shadowRoot', root, root.host, root.querySelector('button')) }) }) }) })
原因分析
- Shadow DOM内部未完成渲染:
customElements.whenDefined('ion-button')仅能确保ion-button自定义元素已注册,但Ionic组件的内部Shadow DOM内容(比如<button>)可能还未完成渲染,此时查询会返回null。 - 异步操作未正确等待:测试中使用
then()处理异步,但没有确保整个异步流程完成后再执行后续操作,测试可能在Shadow DOM渲染完成前就结束了。 - 缺少变更检测触发:Ionic组件的内部渲染可能需要额外的变更检测,仅初始化时的
fixture.detectChanges()不足以触发完整的Shadow DOM内容渲染。
解决方法
修改测试代码,通过async/await等待异步操作完成,并确保触发足够的变更检测:
it('should emit when dismissed clicked', async () => { spyOn(component.handleDismissClick, 'emit'); // 等待ion-button自定义元素注册完成 await customElements.whenDefined('ion-button'); // 等待fixture内的所有异步操作完成 await fixture.whenStable(); // 再次触发变更检测,确保Shadow DOM内容渲染完毕 fixture.detectChanges(); const ionButton = fixture.debugElement.query(By.css('.submitButton')).nativeElement as HTMLElement; const buttonInShadow = ionButton.shadowRoot?.querySelector('button'); // 验证按钮存在 expect(buttonInShadow).not.toBeNull(); // 模拟点击并验证事件触发 buttonInShadow?.click(); expect(component.handleDismissClick.emit).toHaveBeenCalled(); });
额外提示:如果测试中仍有自定义元素相关报错,可以在TestBed.configureTestingModule中添加schemas: [CUSTOM_ELEMENTS_SCHEMA],允许使用未声明的自定义元素。
内容的提问来源于stack exchange,提问作者Jeremy Thomas
相关产品推荐
相关产品推荐

