web-dev-qa-db-ja.com

Angular 2でのngOnChangesライフサイクルフックのテスト

次のコードを考えて、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_を設定するにはどうすればよいですか?

23
user1448982

私はパーティーに少し遅れていると思いますが、これは将来誰かに役立つかもしれません。

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();
    })


});
51

手動で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デコレータが適切に設定されています。

14
s-f

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});
8
The Aelfinn