Angular Testing - 如果包含在 whenStable 中,则预计不会看到

标签 angular karma-jasmine angular-test

我有一个带标签的组件。我需要导航到一个选项卡,然后测试可查看的输入等。我可以从控制台日志中看到我正在执行的操作正在调用我正在使用以下过程进行监视的函数。我还可以看到这是以正确的顺序发生的。
不幸的是,这会产生一个没有错误的成功测试,根据 ng test 在测试中没有发现任何期望。 (规范没有期望)
将期望放在 whenStable 命令之外会使期望在模糊命令之前运行。

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函数的东西。

For using fakeAsync() efficiently, one must rely on tick() or flush().


也许像这样改变你的测试用例,应该可行。
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();
    })
);
编辑:

Replaced flush() with flushMicrotasks() followed by fixture.detectChanges(), it will allow the DOM to be updated with the response after resolving the Promise. On second thought, you can use this approach with flush() or tick() as well.

关于 Angular Testing - 如果包含在 whenStable 中,则预计不会看到,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62806027/

相关文章:

linux - 设置 karma-cordova (Linux) 时遇到问题

Angular 2 Materials 在 karma 测试中发出警告

angular - 如何在单元测试中模拟 ApplicationRef

angular - 如何在不先构建的情况下运行 ngserve

android - 从设备应用程序调用 http api

html - 排列单选按钮

Angular 8 单元测试,无法设置 null 的属性 'valueAccessor'

angular - d3-cloud 的类型

angular - 运行 headless Chromium 浏览器会抛出错误 'Failed to read DnsConfig'(在 Docker 容器内)

angular - 单元测试 - onGridReady - Angular 中的 Ag-grid 事件