サービスコールと観測可能な呼び出しを使用してデータリストを取得するAngular 2コンポーネントをテストしようとしています。メインのアプリケーションモジュールをこの仕様ファイルにインポートしました。Observablesを使用するAngular 2コンポーネントのジャスミンテストを書く
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』プロパティを読み取ることができません」というエラーを取得していません。
マイspecファイルは次のようになります。
私のコンポーネントのコードは次のようになります。私は何かを欠けている場合
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 { }
は、私は私が観察を考慮して、私のspecファイルにいくつかのコードを欠けているのかはわかりませんか?どんな助けもありがとうございます。
事務所サービス
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>
[docsに表示されている](https://angular.io/docs/ts/latest/guide/testing.html#!#component-with-external-template)の設定を採用しようとしましたか? – jonrsharpe
私はbeforeEachとテスト自体の間でコードを前後に移動しようとしましたが、そこにどのようなセットアップをお勧めしますか?私は設定前にDOMをテストしているので、null nativeElementを取得していますか?私はサービスを模擬するだけで、プロバイダーに本物のものを追加する必要はありませんか? – bschmitty
さて、それを読んでください。* 2つの 'beforeEach'セクションを推奨しています。一つは' async'セクションです。あなたの現在のセットアップで 'createComponent'が期待通りに達する前に起こっていないようです。 – jonrsharpe