web-dev-qa-db-fra.com

Test du hook de cycle de vie ngOnChanges dans Angular 2

Étant donné le code suivant, j'essaie de tester le hook de cycle de vie ngOnChanges d'Angular2:

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

Malheureusement, le test échoue avec le message Expected spy ngOnChanges to have been called. Je sais que je pourrais simplement vérifier le contenu de l'élément HTML dans cet exemple, mais j'ai du code qui doit être testé à l'intérieur du hook de cycle de vie ngOnChanes, donc ce n'est pas une solution pour moi. Je ne veux pas non plus appeler directement testComponent.ngOnChanges({someMockData}); dans le test.

Comment puis-je définir le TestComponent.value À partir d'un test pour que ngOnChanges soit appelé?

23
user1448982

Je suppose que je suis un peu en retard pour la fête, mais cela peut être utile à quelqu'un à l'avenir.

Il y a eu quelques changements dans les tests depuis le RC 5 de angular a été publié. Cependant, le principal problème ici est que ngOnChanges n'est pas appelé lorsque les entrées sont définies par programme. - Voir ceci pour plus d'informations . Fondamentalement, le hook OnChanges est déclenché lorsque les entrées sont passées via la vue uniquement .

La solution à cela serait d'avoir un composant hôte qui serait le parent du composant de test et de passer des entrées via le modèle du composant hôte.

Voici le code de travail complet:

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
Kiran Yallabandi

Vous avez également la possibilité d'appeler ngOnChanges hook manuellement et d'y passer les modifications souhaitées. Mais cela ne définit pas les propriétés du composant, n'appelle que la logique de changement.

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

Sachez que cette approche fonctionnera correctement pour tester la logique à l'intérieur de ngOnChanges, mais elle ne testera pas que le @Input les décorateurs sont correctement configurés.

14
s-f

Dans Angular 4, pour déclencher manuellement ngOnChanges() lors du test, vous devrez effectuer manuellement l'appel (comme indiqué ci-dessus), vous seul devez faire correspondre le - nouvelle signature d'appel de 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