测试覆盖率无法涵盖测试条件

时间:2020-01-21 11:53:47

标签: angular jasmine

我已经使用茉莉花在Angular 8中编写了一个测试。目前以代码覆盖率报告抱怨以下条件未被覆盖。我写了一个测试,下面我认为涵盖了该测试,但似乎并未涵盖该测试。标记为适合的测试是我为了满足该条件而执行的测试。可能是什么问题

确认方法中未包含以下条件

&& this.permissions.ViewNotes

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

测试组件

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 show the comments box when accept confirmation is called with compliance role', () => {
    setupComponent();

    var mockNgxPermissionsService  = new MockNgxPermissionsService();
    component.permissions =   mockNgxPermissionsService.getPermissions();
    component.message = 'Approved because potato';

    fixture.ngZone.run(() => component.confirmation('Approve'));

    expect(component.showMessage).toBe(true);
  });

  fit('should set ViewNotes permission when accept confirmation is called with compliance role', () => {
    setupComponent();


    var mockNgxPermissionsService  = new MockNgxPermissionsService();
    component.permissions =  mockNgxPermissionsService.getPermissions();
    component.message = 'Approved because potato';

    fixture.ngZone.run(() => component.confirmation('Approve'));

    expect(component.permissions.ViewNotes).not.toBeNull;

  });


});

组件

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);
    }

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


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

1 个答案:

答案 0 :(得分:0)

在您的规格文件中,您有以下内容:

const mockRoute = { params: of({ id: '123', type: 'test' }), snapshot: {} };

您必须将类型更改为“批准”才能测试 && this.permissions.ViewNotes 方案。它没有覆盖,因为您使用了 '&&'运算符,因此无需检查第二个条件即可转到下一条语句。我认为您可以创建另一个描述并实现这种情况。