Vincent Barrault

Tester la méthode ngOnChange d'un composant Angular

Tester la méthode ngOnChange d'un composant Angular

Table des matières

Créer un composant à tester

Pour commencer, nous allons créer un nouveau projet avec un composant qui nous servira de base pour le test.

ng new testing-project
cd testing-project
ng generate component StringWithCounter

Ce composant aura un comportement très simple, il prendra en paramètre une chaîne de caractères, il comptera le nombre de lettres de cette chaîne, et il affichera la chaîne de caractères et le nombre de lettres dans le template.

./app/string-with-counter.component.ts
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';

@Component({
    selector: 'app-string-with-counter',
    template: `<p>{{ theString }} (length: {{ theStringCount }})</p>`,
})
export class StringWithCounterComponent implements OnChanges {

    /**
     * The string to compute.
     */
    @Input() theString?: string;

    /**
     * The computed length of the string.
     */
    public theStringCount = 0;

    /**
     * Check for changes in the string and compute the length of the string.
     */
    ngOnChanges(changes: SimpleChanges): void {
        if (changes.theString) {
            this.theStringCount = changes.theString.currentValue ? changes.theString.currentValue.length : 0;
        }
    }
}

Tester notre composant

Pour tester notre composant, nous allons lui passer une chaîne de caractères de test et vérifier que la taille calculée est bien celle attendue.

Deux possibilités s'offrent à nous, la première et la plus simple, est d'appeller directement la méthode ngOnChange avec une réplique de ce que le cycle de vie du composant aurait envoyé.

./app/string-with-counter.component.spec.ts
import { Component, SimpleChange } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { StringWithCounterComponent } from './string-with-counter.component';

describe('Classical test of the component', () => {

    let component: StringWithCounterComponent;
    let fixture: ComponentFixture<StringWithCounterComponent>;

    beforeEach(async () => {
        await TestBed.configureTestingModule({
            declarations: [StringWithCounterComponent]
        }).compileComponents();
    });

    beforeEach(() => {
        fixture = TestBed.createComponent(StringWithCounterComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    });

    it('Will not update the count this way.', () => {

        // WHEN
        component.theString = 'Vincent';

        // THEN
        fixture.detectChanges();
        expect(component.theStringCount).toEqual(0);
    });

    it('Should update the count correctly.', () => {

        // WHEN
        component.ngOnChanges({
            theString: new SimpleChange(undefined, 'Vincent', true),
        });

        // THEN
        fixture.detectChanges();
        expect(component.theStringCount).toEqual(7);
    });
});

La deuxième est de créer un composant de test pour appeler notre composant à tester. Cette approche semble plus saine car elle se rapproche plus de l'utilisation normale que l'on ferait de notre composant.

./app/string-with-counter.component.spec.ts
describe('Test with a wrapper component', () => {

    @Component({
        selector: 'app-test-wrapper-component',
        template: `<app-string-with-counter [theString]="testedInput"></app-string-with-counter>`
    })
    class TestWrapperComponent {
        public testedInput?: string;
    }

    let wrapperComponent: TestWrapperComponent;
    let testedComponent: StringWithCounterComponent;
    let fixture: ComponentFixture<TestWrapperComponent>;

    beforeEach(async () => {
        await TestBed.configureTestingModule({
            declarations: [
                StringWithCounterComponent,
                TestWrapperComponent,
            ]
        }).compileComponents();
    });

    beforeEach(() => {
        fixture = TestBed.createComponent(TestWrapperComponent);
        wrapperComponent = fixture.componentInstance;
        testedComponent = fixture.debugElement.query(By.directive(StringWithCounterComponent)).componentInstance;
        fixture.detectChanges();
    });

    it('Update the count correctly.', () => {

        // WHEN
        wrapperComponent.testedInput = 'Vincent';

        // THEN
        fixture.detectChanges();
        expect(testedComponent.theStringCount).toEqual(7);
    });
});

Résultat

Résultat des tests

Il est bien-sûr possible de créer plusieurs composants de tests différents pour simuler plusieurs cas d'usage différents.