我正在尝试使用服务调用和可观察调用来获取数据列表的角度2组件。我已经将我的主应用程序模块导入到此spec文件中。编写使用Observables的Angular 2组件的Jasmine测试
我的规格文件看起来像这样:
import { ComponentFixture, TestBed, fakeAsync, async } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { MaterialModule } from '@angular/material';
import { FormsModule } from '@angular/forms';
import { AppModule } from '../../../src/app/app.module';
import { Observable } from 'rxjs/Observable';
import { Store } from '@ngrx/store';
import { } from 'jasmine';
import { FirmService } from '../../../src/app/containers/dashboard/services/firm.service';
import { FirmListComponent } from '../../../src/app/containers/dashboard/firm-list/firm-list.component';
import { mockFirm1, mockFirm2, mockFirms } from './firm-list.mocks';
import { Firm } from '../../../src/app/containers/dashboard/models/firm.model';
import { FirmState } from '../../../src/app/containers/dashboard/services/firm.state';
describe('Firm List Component',() => {
let fixture: ComponentFixture<FirmListComponent>;
let component: FirmListComponent;
let element: HTMLElement;
let debugEl: DebugElement;
let firmService: FirmService;
let mockHttp;
let stateObservable: Observable<FirmState>;
let store: Store<FirmState>;
let getFirmsSpy;
let getObservableSpy;
// utilizes zone.js in order to mkae function execute syncrhonously although it is asynchrounous
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MaterialModule, FormsModule, AppModule],
declarations: [FirmListComponent],
providers: [FirmService]
})
.compileComponents() // compiles the directives template or any external css calls
.then(() => {
fixture = TestBed.createComponent(FirmListComponent); // allows us to get change detection, injector
component = fixture.componentInstance;
debugEl = fixture.debugElement;
element = fixture.nativeElement;
firmService = fixture.debugElement.injector.get(FirmService);
getObservableSpy = spyOn(firmService, 'stateObservable')
.and.returnValue(new FirmState());
getFirmsSpy = spyOn(firmService, 'getFirms')
.and.returnValue(Observable.of(mockFirms));
});
}));
it('should be defined',() => {
expect(component).toBeDefined();
});
describe('initial display',() => {
it('should not show firms before OnInit',() => {
debugEl = fixture.debugElement.query(By.css('.animate-repeat'));
expect(debugEl).toBeNull();
expect(getObservableSpy.calls.any()).toBe(false, 'ngOnInit not yet called');
expect(getFirmsSpy.calls.any()).toBe(false, 'getFirms not yet called');
});
it('should still not show firms after component initialized',() => {
fixture.detectChanges();
debugEl = fixture.debugElement.query(By.css('.animate-repeat'));
expect(debugEl).toBeNull();
expect(getFirmsSpy.calls.any()).toBe(true, 'getFirms called');
});
it('should show firms after getFirms observable', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
// **I get the correct value here, this is the table headers for the table data below that is showing 0**
var rowHeaderLength = element.querySelectorAll('th').length;
expect(rowHeaderLength).toBe(8);
// **I get 0 for rowDataLength here, test fails**
var rowDataLength = element.querySelectorAll('.animate-repeat').length;
console.log(rowDataLength);
});
}));
it('should show the input for searching',() => {
expect(element.querySelector('input')).toBeDefined();
});
});
});
以上传球,但第二个不,我目前得到一个错误,指出第一个测试“无法读取属性空的‘nativeElement’”。
我的组件的代码如下所示:
import { NgModule, Component, Input, OnInit, OnChanges } from '@angular/core';
import { MaterialModule } from '@angular/material';
import { FlexLayoutModule } from '@angular/flex-layout';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Firm } from '../models/firm.model';
import { FirmService } from '../services/firm.service';
@Component({
selector: 'firm-list',
templateUrl: './firm-list.html',
styles: []
})
export class FirmListComponent implements OnInit {
public selectAll: boolean;
public firms: Array<Firm>;
public filteredFirms: any;
public loading: boolean;
public searchText: string;
private componetDestroyed = false;
// @Input() public search: string;
constructor(public firmService: FirmService) { }
public ngOnInit() {
this.firmService.stateObservable.subscribe((state) => {
this.firms = state.firms;
this.filteredFirms = this.firms;
});
this.getFirms();
}
public getFirms(value?: string) {
this.loading = true;
this.firmService.getFirms(value).subscribe((response: any) => {
this.loading = false;
});
}
}
@NgModule({
declarations: [FirmListComponent],
exports: [FirmListComponent],
providers: [FirmService],
imports: [
MaterialModule,
FlexLayoutModule,
CommonModule,
FormsModule
]
})
export class FirmListModule { }
我不知道如果我错过了一些代码在我的规范文件,以占观察到的,或者如果我失去了别的东西?任何帮助表示赞赏。
事务所服务
import { Observable } from 'rxjs/Rx';
import { Injectable } from '@angular/core';
import { AuthHttp } from 'angular2-jwt';
import { Response } from '@angular/http';
import { Store } from '@ngrx/store';
import { firmActions } from './firm.reducer';
import { FirmState } from './firm.state';
@Injectable()
export class FirmService {
public stateObservable: Observable<FirmState>;
constructor(private $http: AuthHttp, private store: Store<FirmState>) {
// whatever reducer is selected from the store (in line below) is what the "this.store" refers to in our functions below.
// it calls that specific reducer function
// how do I define this line in my unit tests?
this.stateObservable = this.store.select('firmReducer');
}
public getFirms(value?: string) {
return this.$http.get('/api/firm').map((response: Response) => {
this.store.dispatch({
type: firmActions.GET_FIRMS,
payload: response.json()
});
return;
});
}
public firmSelected(firms) {
// takes in an action, all below are actions - type and payload
// dispatches to the reducer
this.store.dispatch({
type: firmActions.UPDATE_FIRMS,
payload: firms
});
}
public firmDeleted(firms) {
this.store.dispatch({
type: firmActions.DELETE_FIRMS,
payload: firms
});
}
}
我公司组件的HTML模板:
<md-card class="padding-none margin">
<div class="toolbar" fxLayout="row" fxLayoutAlign="start center">
<div fxFlex class="padding-lr">
<div *ngIf="anySelected()">
<button color="warn" md-raised-button (click)="deleteSelected()">Delete</button>
</div>
<div *ngIf="!anySelected()">
<md-input-container floatPlaceholder="never">
<input mdInput [(ngModel)]="searchText" (ngModelChange)="onChange($event)" type="text" placeholder="Search" />
</md-input-container>
</div>
</div>
<div class="label-list" fxFlex fxLayoutAlign="end center">
<label class="label bg-purple600"></label>
<span>EDF Model</span>
<label class="label bg-green600"></label>
<span>EDF QO</span>
<label class="label bg-pink800"></label>
<span>LGD Model</span>
<label class="label bg-orange300"></label>
<span>LGD QO</span>
</div>
</div>
<md-card-content>
<div class="loading-container" fxLayoutAlign="center center" *ngIf="loading">
<md-spinner></md-spinner>
</div>
<div *ngIf="!loading">
<table class="table">
<thead>
<tr>
<th class="checkbox-col">
<md-checkbox [(ngModel)]="selectAll" (click)="selectAllChanged()" aria-label="Select All"></md-checkbox>
</th>
<th>
Firm Name
</th>
<th>
Country
</th>
<th>
Industry
</th>
<th>
EDF
</th>
<th>
LGD
</th>
<th>
Modified
</th>
<th>
Modified By
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let firm of filteredFirms; let i = index" class="animate-repeat" [ngClass]="{'active': firm.selected}">
<td class="checkbox-col">
<md-checkbox [(ngModel)]="firm.selected" aria-label="firm.name" (change)="selectFirm(i)"></md-checkbox>
</td>
<td>{{firm.name}}</td>
<td>{{firm.country}}</td>
<td>{{firm.industry}}</td>
<td>
<span class="label bg-purple600">US 4.0</span>
<span class="label bg-green600">US 4.0</span>
</td>
<td>
<span class="label bg-pink800">US 4.0</span>
<span class="label bg-orange300">US 4.0</span>
</td>
<td>{{firm.modifiedOn}}</td>
<td>{{firm.modifiedBy}}</td>
</tr>
</tbody>
</table>
</div>
</md-card-content>
</md-card>
您是否尝试过采用[文档中显示的]设置(https://angular.io/docs/ts/latest/guide/testing.html#!#component-with-external-template)? – jonrsharpe
我试着在beforeEach和测试本身之间来回移动代码,但是在那里推荐什么样的设置?我是否获得了null nativeElement,因为我在测试DOM之前测试了DOM并准备好进行测试?我是否需要模拟服务而不是在提供商中添加真实服务? – bschmitty
好吧,阅读它们 - 他们推荐*两个*'beforeEach'部分,一个'async'不是。看起来像在你当前的设置中'createComponent'在你达到预期之前不会发生。 – jonrsharpe