测试覆盖率无法覆盖笔试

时间:2020-01-20 17:53:49

标签: angular jasmine

我正在使用Jasmine编写针对Angular 8应用程序的测试。我已经为确认对话框编写了测试。显示确认对话框时,我需要检查三个条件。重置,拒绝和批准。当我运行npm test cover进行测试覆盖时,突出显示了下面代码的其他部分未覆盖。我认为我的测试(应该称为拒绝确认对话框)应该已经涵盖了它。 我不确定为什么测试没有涵盖所有内容?有人可以帮忙吗

if (type === 'Approve') {
  this.confirmationDialog.show(this.approvalMessage, type, () => this.confirm(type));
} else if (type === 'Reset') {
  this.confirmationDialog.show(this.resetMessage, type, () => this.confirm(type));
} else if (type === 'Reject') {
  this.confirmationDialog.show(this.rejectMessage, type, () => this.confirm(type));
}

组件代码

export class ApproveComponent {
  public showMessage = false;
  public message: string;
  public body = 'Loading content for approval...';
  public loading = true;
  public permissions: NgxPermissionsObject;
  private id: string;
  private type: string;
  private approvalMessage = 'Are you sure the information is correct and you want to approve?';
  private resetMessage = 'Are you sure you want to reset this approval back to the start?';
  private rejectMessage = 'Are you sure you want to reject this approval? ' +
    'Rejecting an approval is permanent and will close this process and prevent it progressing further.';

  constructor(
    private router: Router,
    private route: ActivatedRoute,
    private service: ApprovalsService,
    private ngxPermissionsService: NgxPermissionsService,
    private messageService: MessageService,
    public confirmationDialog: ConfirmationDialogService) {
    this.route.params.subscribe(params => {
      this.id = params['id'];
      this.type = params['type'];
      this.loading = true;
      this.service.get(this.type, this.id).then(x => {
        if (!x) { return; }
        this.loading = false;
        this.body = x.body;
      });
    });
    this.permissions = this.ngxPermissionsService.getPermissions();
  }




  public confirmation = (type: 'Reset' | 'Reject' | 'Approve') => {
    if (!this.showMessage
      && ((type === 'Approve' && this.permissions.ViewNotes)
        || (type !== 'Approve'))) {
      this.showMessage = true;
      return;
    }

    if (!this.message
      && ((type === 'Approve' && this.permissions.ViewNotes)
        || (type !== 'Approve'))) {
      this.messageService.add('Message is required.', 'warning');
      return;
    }

    if (type === 'Approve') {
      this.confirmationDialog.show(this.approvalMessage, type, () => this.confirm(type));
    } else if (type === 'Reset') {
      this.confirmationDialog.show(this.resetMessage, type, () => this.confirm(type));
    } else if (type === 'Reject') {
      this.confirmationDialog.show(this.rejectMessage, type, () => this.confirm(type));
    }

  }

  public confirm = (type: 'Reset' | 'Reject' | 'Approve') => {
    let promise;

    if (type === 'Reset') {
      promise = this.service.reset(this.id, this.type, this.message);
    } else if (type === 'Reject') {
      promise = this.service.reject(this.id, this.type, this.message);
    } else if (type === 'Approve') {
      promise = this.service.approve(this.id, this.type, this.message);
    }


    promise.then(() => {
      this.messageService.add(`Successfully ${type}.`, 'info');
      this.navigateToApprovalList();
    });
  }


  private navigateToApprovalList = () => this.router.navigate(['/approvals']);
}

测试组件

describe('ApproveComponent', () => {
  let component: ApproveComponent;
  let injector: TestBed;
  let fixture: ComponentFixture<ApproveComponent>;
  const mockService: ApprovalsService = <ApprovalsService>{
    approve: (id: string, type: string, message: string) => <Promise<any>>{},
    reset: (id: string, type: string, message: string) => <Promise<any>>{},
    reject: (id: string, type: string, message: string) => <Promise<any>>{},
    get: (type: string, id: string) => <Promise<any>>{},
  };
  const mockRoute = { params: of({ id: '123', type: 'test' }), snapshot: {} };

  function setupComponent(getResult: any = {}) {
    spyOn(mockService, nameof<ApprovalsService>('approve')).and.returnValue(Promise.resolve({}));
    spyOn(mockService, nameof<ApprovalsService>('reset')).and.returnValue(Promise.resolve({}));
    spyOn(mockService, nameof<ApprovalsService>('reject')).and.returnValue(Promise.resolve({}));
    spyOn(mockService, nameof<ApprovalsService>('get')).and.returnValue(Promise.resolve(getResult));

    TestBed.configureTestingModule({
      imports: [
        DxTextAreaModule,
        DxButtonModule,
        SharedModule,
        RouterTestingModule.withRoutes([{ path: 'approvals', component: ApproveComponent }])
      ],
      declarations: [ApproveComponent],
      providers: [
        { provide: ApprovalsService, useValue: mockService },
        { provide: ActivatedRoute, useValue: mockRoute },
        { provide: MessageService, useClass: MockMessageService },
        { provide: ConfirmationDialogService, useValue: ConfirmationDialogServiceMock },
        { provide: NgxPermissionsService, useClass: MockNgxPermissionsService }
      ]
    })
      .compileComponents();

    fixture = TestBed.createComponent(ApproveComponent);
    injector = getTestBed();
    component = fixture.componentInstance;
    spyOn((<any>component).router, 'navigate').and.returnValue(true);

    fixture.detectChanges();
  }

  it('should create and call get', () => {
    setupComponent();

    expect(component).toBeTruthy();
    expect(mockService.get).toHaveBeenCalled();
  });

  it('should call get and do not set body on empty result', () => {
    setupComponent(null);

    expect(component).toBeTruthy();
    expect(component.body).toBe('Loading content for approval...');
  });

  it('should call confirmation dialog when accept confirmation is called', () => {
    setupComponent();
    const dialogService = injector.get(ConfirmationDialogService);
    const dialogServiceSpy = spyOn(dialogService, 'show').and.callThrough();
    component.showMessage = true;
    component.message = 'Approved because potato';
    fixture.ngZone.run(() => component.confirmation('Approve'));
    expect(dialogServiceSpy).toHaveBeenCalled();
    expect(dialogServiceSpy).toHaveBeenCalled();
  });



   it('should call reject confirmation dialog', () => {
    setupComponent();
    const dialogService = injector.get(ConfirmationDialogService);
    const dialogServiceSpy = spyOn(dialogService, 'show').and.callThrough();

    component.showMessage = true;
    component.message = 'rejected because potato';
    fixture.ngZone.run(() => component.confirmation('Reject'));
    expect(dialogServiceSpy).toHaveBeenCalled();
    expect(mockService.reject).toHaveBeenCalled();
  });

  it('should call reset confirmation dialog', () => {
    setupComponent();
    const dialogService = injector.get(ConfirmationDialogService);
    const dialogServiceSpy = spyOn(dialogService, 'show').and.callThrough();

    component.showMessage = true;
    component.message = 'rejected because potato';
    fixture.ngZone.run(() => component.confirmation('Reset'));
    expect(dialogServiceSpy).toHaveBeenCalled();
    expect(mockService.reset).toHaveBeenCalled();
  });

  it('should set showMessage when reject is called', () => {
    setupComponent();

    component.showMessage = false;
    fixture.ngZone.run(() => component.confirmation('Reject'));
    expect(component.showMessage).toBe(true);
    expect(mockService.reject).not.toHaveBeenCalled();
  });

  it('should not call service when reject is called and message is empty', () => {
    setupComponent();

    component.showMessage = true;
    component.message = '';
    fixture.ngZone.run(() => component.confirmation('Reject'));
    expect(mockService.reject).not.toHaveBeenCalled();
  });
});

1 个答案:

答案 0 :(得分:0)

这是因为每个if语句都必须有其他情况。这是为了防止程序在if true条件下退出。代码覆盖率告诉您最终的“ else”用例未经测试。您需要添加一个测试,以验证如果提供了无效的类型,对话框不会打开。

it('should call reset confirmation dialog', () => {
  setupComponent();
  const dialogService = injector.get(ConfirmationDialogService);
  const dialogServiceSpy = spyOn(dialogService, 'show').and.callThrough();

  component.showMessage = true;
  component.message = 'rejected because potato';
  fixture.ngZone.run(() => component.confirmation('bad-type' as any));
  expect(dialogServiceSpy).not.toHaveBeenCalled();
});