次のコードを考えて、Angular2のngOnChanges
ライフサイクルフックをテストしようとしています。
_import {
it,
inject,
fdescribe,
beforeEachProviders,
} from '@angular/core/testing';
import {TestComponentBuilder} from '@angular/compiler/testing';
import {Component, OnChanges, Input} from '@angular/core';
@Component({
selector: 'test',
template: `<p>{{value}}</p>`,
})
export class TestComponent implements OnChanges {
@Input() value: string;
ngOnChanges(changes: {}): any {
// should be called
}
}
fdescribe('TestComponent', () => {
let tcb: TestComponentBuilder;
beforeEachProviders(() => [
TestComponentBuilder,
TestComponent,
]);
beforeEach(inject([TestComponentBuilder], _tcb => {
tcb = _tcb;
}));
it('should call ngOnChanges', done => {
tcb.createAsync(TestComponent).then(fixture => {
let testComponent: TestComponent = fixture.componentInstance;
spyOn(testComponent, 'ngOnChanges').and.callThrough();
testComponent.value = 'Test';
fixture.detectChanges();
expect(testComponent.ngOnChanges).toHaveBeenCalled();
done();
}).catch(e => done.fail(e));
});
});
_
残念ながら、テストはメッセージ_Expected spy ngOnChanges to have been called.
_で失敗します。この例ではHTML要素の内容を確認するだけでよいことはわかっていますが、ngOnChanesライフサイクルフック内でテストする必要のあるコードがあります。私のためのソリューション。また、テストでtestComponent.ngOnChanges({someMockData});
を直接呼び出したくありません。
ngOnChanges
が呼び出されるように、テストから_TestComponent.value
_を設定するにはどうすればよいですか?
私はパーティーに少し遅れていると思いますが、これは将来誰かに役立つかもしれません。
angularのRC 5がリリースされて以来、テストにいくつかの変更がありました。ただし、ここでの主な問題は、入力がプログラムで設定されたときにngOnChanges
が呼び出されないことです- 詳細はこちらをご覧ください 。基本的に、ビューのみを介して入力が渡されたときに、OnChanges
フックがトリガーされます。
これに対する解決策は、テストコンポーネントの親となるホストコンポーネントを用意し、ホストコンポーネントのテンプレートを介して入力を渡すことです。
完全な作業コードは次のとおりです。
import {Component, OnChanges, Input, ViewChild} from '@angular/core';
import { TestBed } from '@angular/core/testing';
@Component({
selector: 'test',
template: `<p>{{value}}</p>`,
})
export class TestComponent implements OnChanges {
@Input() value: string;
ngOnChanges(changes: {}): any {
// should be called
}
}
/* In the Host component's template we will pass the inputs to the actual
* component to test, that is TestComponent in this case
*/
@Component({
selector : `test-Host-component`,
template :
`<div><test [value]="valueFromHost"></test></div>`
})
export class TestHostComponent {
@ViewChild(TestComponent) /* using viewChild we get access to the TestComponent which is a child of TestHostComponent */
public testComponent: any;
public valueFromHost: string; /* this is the variable which is passed as input to the TestComponent */
}
describe('TestComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({declarations: [TestComponent,TestHostComponent]}); /* We declare both the components as part of the testing module */
});
it('should call ngOnChanges', ()=> {
const fixture = TestBed.createComponent(TestHostComponent);
const hostComponent = fixture.componentInstance;
hostComponent.valueFromHost = 'Test';
const component = hostComponent.testComponent;
spyOn(component, 'ngOnChanges').and.callThrough();
fixture.detectChanges();
expect(component.ngOnChanges).toHaveBeenCalled();
})
});
手動でngOnChanges
フックを呼び出して、必要な変更オブジェクトをそこに渡すオプションもあります。ただし、これはコンポーネントプロパティを設定せず、変更ロジックを呼び出すだけです。
const previousValue = moment('2016-03-01T01:00:00Z');
const currentValue = moment('2016-02-28T01:00:00Z');
const changesObj: SimpleChanges = {
prop1: new SimpleChange(previousValue, currentValue)
};
component.ngOnChanges(changesObj);
このアプローチはngOnChanges
内のロジックをテストするために正常に機能しますが、@Input
デコレータが適切に設定されています。
Angular 4)で、テスト時にngOnChanges()
を手動でトリガーするには、手動で呼び出しを行う必要があります(上記で指摘したように) SimpleChange()の新しい呼び出しシグネチャ :
let prev_value = "old";
let new_value = "new";
let is_first_change: boolean = false;
component.ngOnChanges({prop1: new SimpleChange(prev_value, new_value, is_first_change});