Жасмин: Как смоделировать компонент @ViewChild в Angular 8 - PullRequest
0 голосов
/ 24 октября 2019

Я пытаюсь протестировать компонент с дочерними компонентами, которые также хранятся как @ViewChild

project -valuation.component.ts

import { Component, OnInit, ViewChild, AfterViewInit, OnDestroy } from '@angular/core';
import { ProjectCreateComponent } from './project-create/project-create.component';
import { ProjectCaracComponent } from './project-carac/project-carac.component';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-project-estimation',
  templateUrl: './project-estimation.component.html',
  styleUrls: ['./project-estimation.component.css']
})
export class ProjectEstimationComponent implements OnInit, AfterViewInit, OnDestroy {

  @ViewChild(ProjectCreateComponent, {static: false}) createComp: ProjectCreateComponent;
  @ViewChild(ProjectCaracComponent, {static: false}) caracComp: ProjectCaracComponent;

  public isCreateStepCompleted = false;
  public isCaracStepCompleted = false;
  private subTokens: Subscription[] = [];

  constructor() { }

  public ngOnInit() {
  }

  public ngAfterViewInit(): void {

    this.subTokens.push(this.createComp.projectTypeForm.statusChanges
      .subscribe((status) => {
      this.isCreateStepCompleted = this.isFormStatusValid(status);
    }));

    this.subTokens.push(this.caracComp.projectCaracForm.statusChanges
      .subscribe((status) => {
      this.isCaracStepCompleted = this.isFormStatusValid(status);
    }));
  }

  public ngOnDestroy(): void {
    for (const token of this.subTokens) {
      token.unsubscribe();
    }
  }

  public isFormStatusValid(status: string) {
    return status === 'VALID';
  }
}

project -valuation.component.html

<mat-card>
  <mat-card-title>
    <p>Estimation de projet</p>
  </mat-card-title>

  <mat-card-content>
    <mat-horizontal-stepper linear #stepper>
        <mat-step id="createStep" [completed]="isCreateStepCompleted">
          <ng-template matStepLabel>Créez votre projet</ng-template>
          <app-project-create></app-project-create>
        </mat-step>
        <mat-step id="caracStep" [completed]="isCharacStepCompleted">
          <ng-template matStepLabel>Définissez les characteristiques</ng-template>
          <app-project-charac></app-project-charac>
        </mat-step>
        <mat-step id="quotationStep" [completed]="isQuotationStepCompleted">
          <ng-template matStepLabel>Récupérez votre estimation</ng-template>
          <app-project-quotation></app-project-quotation>
        </mat-step>
    </mat-horizontal-stepper>
  </mat-card-content>
</mat-card>

Теперь я хочу установить не регрессионный тест, чтобы быть уверенным, что если один из дочерних компонентов испускает statusChanges, статус корректно обновляется.

Вот тест, который я пытался настроить

project -valuation.components.spect.ts

class FormGroupMock {

  public status: string;
  public statusChanges: BehaviorSubject<string>;

  public constructor() {

    this.status = 'VALID';
    this.statusChanges = new BehaviorSubject(this.status);
  }

  public setStatus(status: string): void {
    this.status = status;
    this.statusChanges.next(this.status);
  }
}

@Component({
  selector: 'app-project-create',
  template: ''
})
class CreateCompMockComponent {
  public projectTypeForm = new FormGroupMock();
}

@Component({
  selector: 'app-project-carac',
  template: ''
})
class CaracCompMockDComponent {
  public projectCaracForm = new FormGroupMock();
}

describe('ProjectEstimationComponent', () => {
  let component: ProjectEstimationComponent;
  let fixture: ComponentFixture<ProjectEstimationComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        MaterialModule,
        RouterTestingModule.withRoutes(testProjectEstimationRoutes),
        ReactiveFormsModule,
        BrowserAnimationsModule
      ],
      declarations: [
        DummyComponent,
        CreateCompMockComponent,
        CaracCompMockDComponent,
        ProjectEstimationComponent,
      ],
      providers: [
      ]
    })
    .compileComponents().then(() => {
      fixture = TestBed.createComponent(ProjectEstimationComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
    });
  }));

  it('should be created', () => {
    expect(component).toBeTruthy();
  });
}

Однако, когда я запускаю этотtest i get:

Failed: Uncaught (in promise): TypeError: Cannot read property 'projectTypeForm' of undefined
TypeError: Cannot read property 'projectTypeForm' of undefined
    at ProjectEstimationComponent.ngAfterViewInit (http://localhost:9876/_karma_webpack_/src/app/project-estimation/project-estimation.component.ts:32:41)
...

Итак, как вы можете видеть, фиктивные компоненты не используются в качестве ViewChild для основного компонента. Как я могу это исправить, чтобы я мог использовать эти фиктивные компоненты , чтобы написать более сложный тест, подобный этому:

it('should listen for sub component changes', () => {
    const createComp: CreateCompMockComponent = TestBed.get(ProjectCreateComponent);
    const caracComp: CaracCompMockDComponent = TestBed.get(ProjectCaracComponent);

    expect(component.isCreateStepCompleted).toBe(true);
    expect(component.isCaracStepCompleted).toBe(true);

    createComp.projectTypeForm.setStatus('INVALID');
    caracComp.projectCaracForm.setStatus('INVALID');

    fixture.detectChanges();

    expect(component.isCreateStepCompleted).toBe(false);
    expect(component.isCaracStepCompleted).toBe(false);
  });

1 Ответ

0 голосов
/ 27 октября 2019

Эта ссылка покажет вам пример того, как это сделать, в частности, в разделе Adding a provider to the stub component. По сути, ваш макет должен быть предоставлен сам, что позволит вашему тесту создать viewChild, как вы ожидаете.

class FormGroupMock {

  public status: string;
  public statusChanges: BehaviorSubject<string>;

  public constructor() {

    this.status = 'VALID';
    this.statusChanges = new BehaviorSubject(this.status);
  }

  public setStatus(status: string): void {
    this.status = status;
    this.statusChanges.next(this.status);
  }
}

@Component({
  selector: 'app-project-create',
  template: '<div></div>',
  providers: [{ provide: ProjectCreateComponent, useClass: CreateCompMockComponent }],
})
class CreateCompMockComponent {
  public projectTypeForm = new FormGroupMock();

  setStatus(status: string): void {

  }
}

@Component({
  selector: 'app-project-charac',
  template: '<div></div>',
  providers: [{ provide: ProjectCaracComponent, useClass: CaracCompMockDComponent }],
})
class CaracCompMockDComponent {
  public projectCaracForm = new FormGroupMock();

  setStatus(status: string): void {

  }
}

fdescribe('ProjectEstimationComponent', () => {
  let component: ProjectEstimationComponent;
  let fixture: ComponentFixture<ProjectEstimationComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        // MaterialModule,
        // RouterTestingModule.withRoutes(testProjectEstimationRoutes),
        ReactiveFormsModule,
        BrowserAnimationsModule,
      ],
      declarations: [
        // DummyComponent,
        CreateCompMockComponent,
        CaracCompMockDComponent,
        ProjectEstimationComponent,
      ],
      providers: [
      ],

    })
      .compileComponents().then(() => {
        fixture = TestBed.createComponent(ProjectEstimationComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
      });
  }));

  it('should be created', () => {
    expect(component).toBeTruthy();
  });

  it('should listen for sub component changes', () => {
    // these don't work at the moment, but that can be fixed more easily  
    expect(component.isCreateStepCompleted).toBe(true);
    expect(component.isCaracStepCompleted).toBe(true);

    component.createComp.setStatus('INVALID');
    component.caracComp.setStatus('INVALID');

    fixture.detectChanges();

    expect(component.isCreateStepCompleted).toBe(false);
    expect(component.isCaracStepCompleted).toBe(false);
  });
});

Здесь я сократил ваш html только до затронутых компонентов. Вы можете начать добавлять обратно, как считаете нужным.

<app-project-create></app-project-create>

<app-project-charac></app-project-charac>
...