我有一个canDeactivateGuard,它从组件的MatDialog返回未保存的操作. 我的问题是我无法测试功能的警卫和得到错误— TypeError:Cannot read properties of undefined(正在读取'canUserExitAlertDialog')

这里是守卫—

import { CanDeactivateFn } from '@angular/router';
import { Observable, map } from 'rxjs';

export const canDeactivateGuard: CanDeactivateFn<any> = (
  component,
  route,
  state
): Observable<boolean> | boolean => {
  return component.canUserExitAlertDialog('back').pipe(
    map((result) => {
      console.log(result);
      return result === 'discard';
    })
  );
};

下面是守卫正在调用的组件的方法— 注意:我有一个mat—dialog有两个按钮—'丢弃'和'取消'.点击"丢弃",用户被重定向到主页.

canUserExitAlertDialog(key: string): Observable<any> { 
//I have a condition in the alert component based on this key
    if (this.hasFormSaved) { //if any changes are saved then not considered
      return of('discard');
    }
    const dialogConfig = new MatDialogConfig();
    dialogConfig.disableClose = true;
    dialogConfig.autoFocus = true;
    dialogConfig.data = { action: key, redirect: 'home' };
    if (this.wasFormChanged || this.form.dirty) {
      const dialogRef = this.dialog.open(AlertComponent, dialogConfig);
      return dialogRef.afterClosed();
    } else {
      this.dialog.closeAll();
      return of('discard');
    }
  }

对话框alert 组件中的代码:

export class AlertComponent {
  userAction!: any;
  alertMsg = 'Are you sure you want to discard the changes?';
  unsavedChanges = 'This will reload the page';
  constructor(
    @Inject(MAT_DIALOG_DATA) data: any,
    @Inject(Window) private window: Window,
    private dialogRef: MatDialogRef<AlertComponent>
  ) {
    this.userAction = data;
  }

  public onCancel(): void {
    this.dialogRef.close('cancel');
  }

  public onDiscard(): void {
    this.dialogRef.close('discard');
    if (this.userAction.action === 'something') { //key I passed from the main component
      console.log('do something');
   }
  }
}

最后,这里是我在CanDeactivate规范文件中的代码—

describe('canDeactivateGuard functional guard', () => {
  let nextState: RouterStateSnapshot;
  let component: MyComponent;
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        {
          provide: ActivatedRoute,
          useValue: {
            snapshot: {},
          },
        },
      ],
    });
  });

  it('should be created', fakeAsync(() => {
    const activatedRoute = TestBed.inject(ActivatedRoute);
    const nextState = {} as RouterStateSnapshot;
    const currState = {} as RouterStateSnapshot;
    const guardResponse = TestBed.runInInjectionContext(() => {
      canDeactivateGuard(
        component,
        activatedRoute.snapshot,
        currState,
        nextState
      ) as Observable<any>;
    });
    expect(guardResponse).toBeTruthy();
  }));

我try 创建一个存根组件并定义canUserExitAlertDialog方法,但没有帮助. 有没有其他方法可以成功地完成这个测试?根据Angular ,类级停用防护被弃用.

这里的错误— error message

测试覆盖范围— enter image description here

推荐答案

我们还可以模拟组件以返回一个观察对象,然后使用fakeAsyncflush来接收响应

  it('should be created', fakeAsync(() => {
    const activatedRoute = TestBed.inject(ActivatedRoute);
    const nextState = {} as RouterStateSnapshot;
    const currState = {} as RouterStateSnapshot
    let output;
    const guardResponse = TestBed.runInInjectionContext(() => {
      canDeactivateGuard(
        { canUserExitAlertDialog: (param) => of('discard'), } as any, // <- changed here!
        activatedRoute.snapshot,
        currState,
        nextState
      ).subscribe((data) => {
        output = data;
     });
    });
    flush();
    expect(output).toBeTruthy();
  }));

由于您没有初始化组件,您将收到此错误.至少有两种方法可以向前迈进.

try 将组件初始化为实例,并判断测试是否通过!

it('should be created', fakeAsync(() => {
    const activatedRoute = TestBed.inject(ActivatedRoute);
    const nextState = {} as RouterStateSnapshot;
    const currState = {} as RouterStateSnapshot;
    const guardResponse = TestBed.runInInjectionContext(() => {
      canDeactivateGuard(
        new AlertComponent(), // <- changed here!
        activatedRoute.snapshot,
        currState,
        nextState
      ) as Observable<any>;
    });
    expect(guardResponse).toBeTruthy();
  }));

将组件添加到测试床,创建组件并注入它!

it('should be created', fakeAsync(() => {
    const activatedRoute = TestBed.inject(ActivatedRoute);
    const nextState = {} as RouterStateSnapshot;
    const currState = {} as RouterStateSnapshot;
    const fixture = TestBed.createComponent(FeedbackCardComponent); // <- changed here!
    component = fixture.componentInstance; // <- changed here!
    const guardResponse = TestBed.runInInjectionContext(() => {
      canDeactivateGuard(
        component,
        activatedRoute.snapshot,
        currState,
        nextState
      ) as Observable<any>;
    });
    expect(guardResponse).toBeTruthy();
  }));

Angular相关问答推荐

Transloco不加载Angular 17中的翻译

在Angular中将路由解析器提供程序和组件提供程序相结合

Rxjs重置执行后的可观察延迟并重新调度Angular

Angular 信号 - 使用 mutate() 与使用 forEach() react 性

Angular 12 区域环境切换器

如果observableA在1秒前触发,则过滤掉observableB

如何访问父组件中提供的 TemplateRef 内的服务?

当访问令牌在 MSAL for Angular 中过期时如何重定向用户?

rxjs 这种常见的状态管理模式有名称吗?它有限制吗?

我应该在 Jasmine 3 中使用什么来代替 fit 和 fdescribe?

angular4应用程序中输入的最小值和最大值

获取从(keypress)angular2按下的键

当父组件上的变量发生变化时重新加载子组件

如何设置背景 colored颜色 IONIC 4

Angular:找不到不同的支持对象[object Object]

Angular2延迟加载模块错误'找不到模块'

如何在 Angular cli 项目中包含来自 node_modules 的assets

如何从materialAngular使用分页器?

Angular 4 material表突出显示一行

如何在 Angular 中使用带有 SASS 的 Bootstrap 4