Angular11-12基于Jasmine测试MatDialog的afterClosed订阅报错咨询
问题根源与修复方案
核心报错原因
你遇到的TypeError: Cannot read property 'close' of undefined和afterClosed不触发的问题,主要是4个问题叠加导致的:
- 测试中spy时机错误:你在点击按钮触发
modificar方法前,就spy了初始化时注入的dialogRef实例,但modificar方法执行时会将this.dialogRef重写为dialog.open()的返回值,你提前spy的旧实例根本不会被用到 MatDialogMock的open方法返回值不符合预期:你当前mock的open方法返回的是一个只有afterClosed的临时对象,没有close方法,也不是你定义的MatDialogRefMock实例,赋值给dialogRef后调用close自然报错- spy
dialog.open时没有加callThrough:默认spy会替换原方法实现,你mock的open方法根本不会执行,导致dialogRef一直是初始化注入的旧值,甚至没被重新赋值 - 组件代码变量名写错:
afterClosed的回调参数是resultados,但你判断的是未定义的resul,就算订阅触发也走不到服务调用的逻辑里
分步修改
1. 修复组件代码变量名问题
this.dialogRef.afterClosed().subscribe( // 把回调参数名和判断变量统一 resultados => { if(resultados){ // 原来的resul是未定义变量,改为resultados this.service.realiza(resultados).subscribe( // 原有逻辑不变 ) } } );
额外说明:你的组件是打开对话框的父组件,不需要在构造函数里注入MatDialogRef,这部分属于冗余代码可以删掉,只有对话框本身才需要注入MatDialogRef。
2. 修正MatDialogMock的实现
让open方法返回MatDialogRefMock实例,保证有close方法:
export class MatDialogMock { open() { // 返回你定义的MatDialogRefMock实例,而不是临时对象 return new MatDialogRefMock(); } }
如果需要自定义关闭返回值,可以给MatDialogRefMock加属性控制:
export class MatDialogRefMock { // 自定义关闭返回值 closeResult:any = { group: { id: "1CESO", refFrame: "B" } }; close():void{} afterClosed(): Observable<any>{ return of(this.closeResult); } }
3. 修正测试用例逻辑
it('点击按钮打开对话框,关闭后调用服务提交修改', ()=>{ const spy1 = spyOn(component, 'modificar').and.callThrough(); // spy dialog.open时要加callThrough,才会执行你mock的open方法 const spy2 = spyOn(component["dialog"], 'open').and.callThrough(); const service = fixture.debugElement.injector.get(CustomService); const spy4 =spyOn(service, 'realiza').and.callThrough(); fixture.detectChanges(); // 点击按钮触发modificar方法 const botonEditar= fixture.debugElement.query(By.css('#but')); botonEditar.nativeElement.click(); fixture.detectChanges(); // 此时dialogRef已经被赋值为open返回的MatDialogRefMock实例,不需要手动调用close // 因为afterClosed返回的是of(),是立即发射值的冷observable,订阅后已经触发回调逻辑 expect(spy1).toHaveBeenCalled(); expect(spy2).toHaveBeenCalled(); // 验证服务被调用,且参数正确 expect(spy4).toHaveBeenCalledOnceWith({ group: { id: "1CESO", refFrame: "B" } }); });
额外说明
不需要手动调用component["dialogRef"].close(),因为你的mock里afterClosed直接返回of(...),只要订阅就会立即执行回调逻辑,完全模拟了对话框关闭返回结果的行为。
内容的提问来源于stack exchange,提问作者DavidB
相关产品推荐
相关产品推荐

