我正在尝试使用也存储为@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>
现在,我想设置一个非回归测试,以确保如果子组件之一发出statusChanges,则状态将正确更新。
这是我尝试设置的测试
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();
});
}
但是,当我运行此测试时,我得到了:
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);
});
答案 0 :(得分:0)
This link将向您展示如何执行此操作的示例,特别是在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>