Jasmin : How to mock @ViewChild component in Angular 8

Discussão em 'Angular' iniciado por LuftWaffle, Janeiro 17, 2025.

  1. LuftWaffle

    LuftWaffle Guest

    i'm trying to test a component with child components which are also stored as @ViewChild

    project-estimation.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-estimation.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>


    Now i want to set a non regression test to be sure that if one of the child component emits statusChanges, the status is correctly updated.

    Here is the test i tried to set up

    project-estimation.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();
    });
    }


    However when i run this 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)
    ...


    So as you can see, the mock components are not used as ViewChild for the main component. How can i fix this so i can use those mock components in order to write more complex test like this :

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

    Continue reading...

Compartilhe esta Página