单元测试报错:Cannot read property 'open' of undefined 问题求助
Angular单元测试报错:Cannot read property 'open' of undefined 解决方法
组件代码
openUpdate(rowSelected: iData) { let dialogRef = this.dialog.open(DialogEditComponent, {}); dialogRef.componentInstance.dialogRef = dialogRef; dialogRef.componentInstance.selectedData = rowSelected; const sub = dialogRef.componentInstance.onAdd.subscribe((data: iData) => { if (data) { this.update(data); } }); dialogRef.afterClosed().subscribe(() => { sub.unsubscribe(); }); }
测试代码(spec.ts)
beforeEach(async () => { const dialogSpy = { open: jasmine.createSpy('open').and.returnValue({ componentInstance: { onAdd: jasmine.createSpyObj('onAdd', ['subscribe']) }, afterClosed: () => { return jasmine.createSpyObj('afterClosed', ['subscribe']); } }) }; toastrServiceSpy = jasmine.createSpyObj('ToastrService', ['success', 'error']); await TestBed.configureTestingModule({ declarations: [Component1, DialogEditComponent], imports: [MatDialogModule, BrowserAnimationsModule], providers: [ { provide: MatDialog, useValue: dialogSpy }, { provide: ToastrService, useValue: toastrServiceSpy }, { provide: MAT_DIALOG_DATA, useValue: {} }, ], }).compileComponents(); }); // ... it('should open the edit dialog with the selected data', async () => { await component.openUpdate(mockData[0]); expect(dialogSpy.open).toHaveBeenCalled(); });
错误信息
TypeError: Cannot read property 'open' of undefined at UserContext.<anonymous> (src/app/features/component1.component.spec.ts:99:22) at Generator.next (<anonymous>) at asyncGeneratorStep (node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:3:1) at apply (node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:22:1) at _ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:409:30) at ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/zone-testing.js:303:43) at _ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:408:56) at Zone.run (node_modules/zone.js/dist/zone.js:169:47) at apply (node_modules/zone.js/dist/zone.js:1326:38) at _ZoneDelegate.invokeTask (node_modules/zone.js/dist/zone.js:443:35)
问题根源
dialogSpy是beforeEach函数内部的局部变量,测试用例(it块)不在它的作用域范围内,所以当你在it块里调用expect(dialogSpy.open)时,dialogSpy是undefined,直接触发报错。另外,你还漏了在beforeEach里实例化组件并触发变更检测,可能会影响依赖注入的完成。
修复方案
把dialogSpy提升为测试文件的顶层变量,同时完善组件实例化流程:
// 把变量提到顶层,让所有测试用例都能访问 let dialogSpy: any; let toastrServiceSpy: any; let component: Component1; let fixture: ComponentFixture<Component1>; beforeEach(async () => { dialogSpy = { open: jasmine.createSpy('open').and.returnValue({ componentInstance: { onAdd: jasmine.createSpyObj('onAdd', ['subscribe']), // 提前定义组件实例里用到的属性,避免后续潜在报错 dialogRef: null, selectedData: null }, afterClosed: () => jasmine.createSpyObj('afterClosed', ['subscribe']) }) }; toastrServiceSpy = jasmine.createSpyObj('ToastrService', ['success', 'error']); await TestBed.configureTestingModule({ declarations: [Component1, DialogEditComponent], imports: [MatDialogModule, BrowserAnimationsModule], providers: [ { provide: MatDialog, useValue: dialogSpy }, { provide: ToastrService, useValue: toastrServiceSpy }, { provide: MAT_DIALOG_DATA, useValue: {} }, ], }).compileComponents(); // 实例化组件并触发变更检测,确保依赖注入完成 fixture = TestBed.createComponent(Component1); component = fixture.componentInstance; fixture.detectChanges(); }); it('should open the edit dialog with the selected data', async () => { await component.openUpdate(mockData[0]); expect(dialogSpy.open).toHaveBeenCalled(); });
修改后,dialogSpy在it块里就能正常访问,同时组件的this.dialog也能正确注入Spy对象,测试就能正常运行了。
内容的提问来源于stack exchange,提问作者Kenzo_Gilead
相关产品推荐
相关产品推荐

