我有一个带有标签的组件。我需要导航到一个选项卡,然后测试可见的输入等。从控制台日志中可以看到,我正在执行的操作正在使用以下过程调用正在监视的函数。我还可以看到这是以正确的顺序发生的。
不幸的是,这会产生成功的测试而没有错误,根据ng test,在测试中没有发现期望。 (SPEC没有期望)
将期望值放置在whenStable命令之外会使期望值在blur命令之前运行。
it('should call to save changes when project name is blurred',
fakeAsync(() => {
component.ngOnInit();
tick();
const tabLabels = fixture.debugElement.queryAll(By.css('.mat-tab-label'));
console.log(tabLabels);
tabLabels[1].triggerEventHandler('click', null);
fixture.detectChanges();
fixture.whenStable().then(() => {
const projectName = fixture.debugElement.query(By.css('#projectName'));
console.log(projectName);
let mySpy = spyOn(component, 'saveProject');
projectName.triggerEventHandler('blur', null);
console.log('Lowered expectations');
expect(mySpy).toHaveBeenCalled();
});
})
);
这是与测试关联的HTML。 <!-- HEADER -->
<div class="header accent" fxLayout="row"></div>
<!-- / HEADER -->
<!-- CONTENT -->
<div class="content">
<!-- CENTER -->
<div class="center p-24" fusePerfectScrollbar>
<!-- CONTENT -->
<div class="content p-24" style="box-shadow:0px 0px 0px rgba(0,0,0,0) !important;">
<mat-tab-group fxFlex="80%"
style="margin:0px 10%">
<mat-tab id="projectSelectionTab" label="Project Selection">
<div fxFlex="80%"
fxLayout="column"
style="margin:0px 10%"
*ngIf="versionList && projectList">
<h1 class="mt-32 mb-20">Select a Project</h1>
<mat-divider></mat-divider>
<div *ngIf="projectList.length==0">
You currently have no designs. Go to the marketplace and select a design to add to your list of projects.
</div>
<table class="displayTable">
<tr (click)="setCurrentProject( project ) "
target="_blank"
class="projectRow"
[ngClass]="{'currentItem' : project==currentProject}"
*ngFor="let project of projectList">
<td class="mat-subheading-2"
style="width:10%">
<mat-icon style="cursor:pointer"
matTooltip="Open in design studio"
[routerLink]="['/designStudio/project/'+project.uid]">
color_lens
</mat-icon>
<mat-icon style="cursor:pointer"
matTooltip="View Quotes for this project"
[routerLink]="['/invoice/'+versionList[versionList.length-1]['uid']]">
attach_money
</mat-icon>
</td>
<td class="mat-subheading-2"
style="width:25%">
{{ project.name }}
</td>
<td class="mat-subheading-2"
style="width:20%">
{{ project.dateCreated | date:'short' }}
</td>
<td class="mat-body-1" >
<span style="font-weight:bold; margin-right:10px;">Type : {{project.designType}}</span>
<span style="font-weight:bold; margin-right:10px;">Versions : {{project.versions.length}}</span>
{{ project.description }}
</td>
</tr>
</table>
</div>
</mat-tab>
<mat-tab id="projectDataTab" label="Project Data" flex="100%">
<div flex="100%"
layout="column"
style="width:100%"
*ngIf="currentProject.uid">
<h2 class="mt-32 mb-20">
Project Data -
<mat-icon style="cursor:pointer"
matTooltip="Open in design studio"
[routerLink]="['/designStudio/project/'+currentProject.uid]">
color_lens
</mat-icon>
</h2>
<!-- TOP ROW OF PROJECT DATA -->
<div fxLayout="row" fxLayoutAlign="center" flex="100%">
<div fxFlex="50%"fxLayout="column" class="text-center">
<mat-form-field appearance="outline" floatLabel="always" class="w-100-p">
<mat-label>Project Name</mat-label>
<input matInput
id="projectName"
class="form-control"
placeholder="Product Name"
name="projectName"
#projectName="ngModel"
[(ngModel)]="currentProject['name']"
(blur)="saveProject()"
minlength="5"
maxlength="150"
required>
</mat-form-field>
<div [hidden]="projectName.valid || projectName.pristine || !projectName.errors?.minlength"
class="alert alert-danger">
Project name is required with at least 5 characters
</div>
<mat-form-field style="min-height:190px" appearance="outline" floatLabel="always" class="w-100-p">
<mat-label>Project Description</mat-label>
<textarea style="min-height:190px"
matInput
id="projectDescription"
placeholder="Product Description"
name="description"
#projectDescription="ngModel"
[(ngModel)]="currentProject['description']"
(blur)="saveProject()"
rows="5"
required
minlength="25">
</textarea>
</mat-form-field>
<div [hidden]="projectDescription.valid || projectDescription.pristine || !projectDescription.errors?.minlength"
class="alert alert-danger">
Project description is required with at least 25 characters
</div>
</div>
<div fxFlex="50" fxLayout="column" fxLayoutAlign="center center">
<div fxFlex="80" style="margin:0px 10%">
<img [src]="(designImageUrl | async)">
</div>
<div fxLayout="row"
fxLayoutAlign="center"
class="w-80-p">
<button mat-raised-button
color="primary"
*ngIf="changesExist"
(click)="saveProject(); changesExist=false;"
style="margin:10px 25%; width:50%">
Save Project Changes
</button>
</div>
</div>
</div>
</div>
<!-- / TOP ROW OF PROJECT DATA -->
</mat-tab>
<mat-tab id="versionDataTab" label="Version Data">
<div layout="column"
flex="100%"
style="width:100%"
*ngIf="currentProject !== undefined">
<h2 class="mt-32 mb-20">Version Data</h2>
<!-- Row showing button to create default version -->
<div fxLayout="row"
fxLayoutAlign="center center"
flex="100%">
<div fxFlex="20" style="margin:20px 5%">
<button mat-stroked-button
color="accent"
(click)="createNewVersion('default')">
New version (default)
</button>
</div>
</div>
<!-- Row showing the version list -->
<div fxLayout="row" fxLayoutAlign="center" flex="100%">
<table class="displayTable"
fxFlex="60" style="margin:0px 5%">
<thead>
<tr>
<th>VERSION NUMBER AND NAME</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let version of versionList; index as j"
class="projectRow"
[ngClass]="{'currentItem' : version==currentVersion}"
(click)="onVersionSelected( j )">
<td>{{j+1}} - {{version.name}}</td>
<td>
<mat-icon style="cursor:pointer"
matTooltip="View Quotes for this version"
[routerLink]="['/invoice/'+version['uid']]">
attach_money
</mat-icon>
<mat-icon style="cursor:pointer"
matTooltip="Make copy as latest version"
(click)="createNewVersion( j )">
add_circle
</mat-icon>
</td>
</tr>
</tbody>
</table>
<!--
<div fxFlex="40" style="margin:0px 5%">
<mat-form-field class="w-100-p ml-10-p mt-20 form-group">
<mat-label>Select a version to see the details</mat-label>
<mat-select (selectionChange)="onVersionSelected(versionIndex)"
name="versionIndex"
#name="ngModel"
[(ngModel)]="versionIndex">
<mat-option *ngFor="let version of versionList; index as i" [value]="i">
{{i+1}} - {{version.name}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div fxLayoutAlign="center center" fxFlex="40" style="margin:0px 5%">
<button mat-button class="red-900 mt-8" *ngIf="currentVersion.latest">Submit for purchase</button>
<button mat-button class="primary mt-8" *ngIf="!currentVersion.latest">Recreate as latest version</button>
</div>
-->
</div>
<div fxLayout="row" fxLayoutAlign="center" flex="100%">
<!-- VERSIONS DATA -->
<div fxLayout="column"
class="mt-32"
flex="45%"
style="width:40%; margin:0px 5%">
<h3>Data</h3>
<div fxLayout="row"
class="mt-20 mb-32">
<div fxFlex="50">
Estimated price - {{currentVersion.price}}
</div>
<div fxFlex="50">
Date Created - {{currentVersion.dateCreated | date:'short'}}
</div>
</div>
<mat-form-field appearance="outline"
floatLabel="always"
class="w-100-p">
<mat-label>Version Name</mat-label>
<input matInput
class="form-control"
placeholder="Version Name"
name="vName"
#versionName="ngModel"
(blur)="versionChangesExist=true"
[(ngModel)]="currentVersion.name"
required
minlength="5"
maxlength="150">
</mat-form-field>
<div [hidden]="versionName.valid || versionName.pristine || !versionName.errors?.minlength"
class="alert alert-danger">
Version name is required with at least 5 characters
</div>
<mat-form-field appearance="outline"
floatLabel="always"
class="w-100-p">
<mat-label>Version Description</mat-label>
<textarea matInput
placeholder="Version Description"
class="form-control"
name="versionDescription"
#versionDescription="ngModel"
(blur)="versionChangesExist=true"
[(ngModel)]="currentVersion.description"
rows="5"
required>
</textarea>
</mat-form-field>
<div [hidden]="versionDescription.valid || versionDescription.pristine || !versionDescription.errors?.minlength"
class="alert alert-danger">
Version description is required with at least 25 characters
</div>
</div>
<!--/ VERSION DATA -->
<!-- MEASUREMENT LIST -->
<div fxLayout="column"
class="mt-32"
flex="45%"
style="width:40%; margin:0px 5%">
<div fxLayout="row" fxLayoutAlign="center" flex="100%">
<button mat-raised-button
color="primary"
*ngIf="versionChangesExist"
(click)="saveVersion(); versionChangesExist=false;"
style="margin:10px 15%; width:70%">
Save Version
</button>
</div>
<h3>Measurements</h3>
<table mat-table [dataSource]="currentVersion.measurements"
class="mat-elevation-z8"
style="box-shadow:none;">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef> Name </th>
<td mat-cell *matCellDef="let meas"> {{meas.name}} </td>
</ng-container>
<ng-container matColumnDef="value">
<th mat-header-cell *matHeaderCellDef> Value </th>
<td mat-cell *matCellDef="let meas"> {{meas.value}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="columnsToDisplayMeas"></tr>
<tr mat-row *matRowDef="let row; columns: columnsToDisplayMeas;"></tr>
</table>
</div>
<!--/ MEASUREMENT LIST -->
</div>
</div>
</mat-tab>
<mat-tab id="designAndPurchaseTab" label="Design and Purchase Status">
<div flex="100%"
layout="column"
style="width:100%"
*ngIf="currentProject !== undefined">
<h2 class="mt-32 mb-20">Design and Purchase Status</h2>
<div fxLayout="row"
fxLayoutAlign="center center">
<div *ngFor="let stage of projectStages; index as j;"
fxLayoutAlign="center center"
[class]="projectStatus[j] ? 'arrow_box_green' : 'arrow_box_red' "
[ngStyle]="{'z-index':1000-j}"
(click)="setSelected( j )">
<span>{{stage}}</span>
</div>
</div>
<div fxLayout="row"
fxLayoutAlign="center center">
<div *ngFor="let stage of projectStages; index as j;"
fxLayoutAlign="center center">
<div [class]="projectStatus[j] ? 'green_right' : '' "
*ngIf="selectedStatus[j]"></div>
<div [class]="!projectStatus[j] ? 'red_right' : '' "
*ngIf="selectedStatus[j]"></div>
<div *ngIf="!selectedStatus[j]"
class="filler"></div>
<div class="filler"></div>
</div>
</div>
<div fxLayout="row"
fxLayoutAlign="center center">
<div *ngFor="let text of stageTexts; index as j;"
fxLayoutAlign="center center">
<div fxFlex="50"
*ngIf="selectedStatus[j] && projectStatus[j]">{{text.done}}</div>
<div fxFlex="50"
*ngIf="selectedStatus[j] && !projectStatus[j]">{{text.notdone}}</div>
</div>
</div>
</div>
</mat-tab>
</mat-tab-group>
</div>
<!-- / CONTENT -->
</div>
<!-- / CENTER -->
</div>
<!-- / CONTENT -->
</div>
最佳答案
这是因为whenStable()
在fakeAsync()
函数中不能很好地与async
函数配合使用。
也许像这样更改您的测试用例,应该可以工作。
it('should call to save changes when project name is blurred',
fakeAsync(() => {
component.ngOnInit();
tick();
const tabLabels = fixture.debugElement.queryAll(By.css('.mat-tab-label'));
console.log(tabLabels);
tabLabels[1].triggerEventHandler('click', null);
fixture.detectChanges();
flushMicrotasks(); // or alternatively flush() or tick(250);
fixture.detectChanges();
const projectName = fixture.debugElement.query(By.css('#projectName'));
console.log(projectName);
let mySpy = spyOn(component, 'saveProject');
projectName.triggerEventHandler('blur', null);
console.log('Lowered expectations');
expect(mySpy).toHaveBeenCalled();
})
);
编辑:关于angular - Angular Testing -预期是否包含在whenStable中,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/62806027/