Cypress Angular组件测试中变更检测的等效方法咨询
我完全理解你的困扰——在Angular单元测试里我们习惯用fixture.detectChanges()手动触发变更检测,但到了Cypress组件测试场景中,之前直接用cdref.detectChanges()在e2e测试里报错,只能靠点击其他元素间接触发,确实有点别扭。
其实在Cypress Angular组件测试中,我们完全可以找到和单元测试里fixture.detectChanges()等效的方案,下面给你几个实用的解决方法:
方法一:挂载组件时获取Fixture实例,直接调用detectChanges
在测试的beforeEach阶段挂载组件时,你可以保存组件对应的ComponentFixture实例,之后在测试逻辑里直接调用它的detectChanges()方法,用法和单元测试几乎一致:
import { ComponentFixture } from '@angular/core/testing'; import { YourComponent } from './your-component.component'; import { ManageService } from './manage.service'; describe('YourComponent', () => { let fixture: ComponentFixture<YourComponent>; let manageService: ManageService; beforeEach(() => { // 挂载组件时,解构获取fixture和服务实例 cy.mount(YourComponent, { providers: [ManageService] }).then(({ fixture: fx, inject }) => { fixture = fx; manageService = inject(ManageService); }); }); it.only('should save data to system', () => { // 更新服务数据 manageService.updateAthlete(true, new Athlete(getMockAthlete())); manageService.updateRegister(true, { belt_id: 5 }); // 直接触发变更检测,替代点击#info的操作 fixture.detectChanges(); // 现在按钮状态已更新,可直接操作 cy.get('[data-cy="save-btn"]').click(); }); });
方法二:通过组件实例调用ChangeDetectorRef
如果你的组件已经注入了ChangeDetectorRef并赋值给了类属性(比如你代码里的this.cdref),也可以直接获取组件实例来调用detectChanges():
describe('YourComponent', () => { let component: YourComponent; let manageService: ManageService; beforeEach(() => { cy.mount(YourComponent, { providers: [ManageService] }).then(({ component: cmp, inject }) => { component = cmp; manageService = inject(ManageService); }); }); it.only('should save data to system', () => { manageService.updateAthlete(true, new Athlete(getMockAthlete())); manageService.updateRegister(true, { belt_id: 5 }); // 调用组件的ChangeDetectorRef触发变更检测 component.cdref.detectChanges(); cy.get('[data-cy="save-btn"]').click(); }); });
方法三:直接注入ChangeDetectorRef调用
你也可以在测试中直接注入ChangeDetectorRef实例,然后调用它的detectChanges()方法:
import { ChangeDetectorRef } from '@angular/core'; it.only('should save data to system', () => { manageService.updateAthlete(true, new Athlete(getMockAthlete())); manageService.updateRegister(true, { belt_id: 5 }); // 注入ChangeDetectorRef并触发变更检测 cy.inject(ChangeDetectorRef).then((cdr) => { cdr.detectChanges(); }); cy.get('[data-cy="save-btn"]').click(); });
为什么之前点击#info能生效?
其实你点击#info元素时,Angular会自动触发一次变更检测循环,所以组件的validCompetitor状态更新后才会同步到按钮的disabled属性上。但手动调用变更检测显然更直接,也不需要依赖额外的用户交互操作,让测试逻辑更清晰。
另外需要说明的是,你之前在e2e测试里用cdref.detectChanges()报错,是因为e2e测试运行在真实浏览器环境中,我们很难直接获取到组件内部的实例和依赖;但Cypress组件测试是运行在Angular的测试环境中,和单元测试的上下文类似,所以可以安全地使用这些变更检测的方法。
备注:内容来源于stack exchange,提问作者Ken

