Подтвердить что ты не робот

Тестирование крюка жизненного цикла ngOnChanges в Angular 2

Учитывая следующий код, я пытаюсь проверить крючок ngOnChanges жизненного цикла 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));
    });
});

К сожалению, тест завершился неудачно с сообщением Expected spy ngOnChanges to have been called. Я знаю, что я мог бы просто проверить содержимое HTML-элемента в этом примере, но у меня есть код, который необходимо протестировать внутри крючка жизненного цикла ngOnChanes, так что решение для меня. Я также не хочу напрямую вызывать testComponent.ngOnChanges({someMockData}); в тесте.

Как я могу установить TestComponent.value из теста, чтобы вызвать ngOnChanges?

4b9b3361

Ответ 1

Думаю, я немного опоздал на вечеринку, однако это может пригодиться кому-то в будущем.

После выпуска RC 5 angular в тестирование было внесено несколько изменений. Однако основная проблема здесь заключается в том, что ngOnChanges не вызывается, когда входы устанавливаются программно. Смотрите это для получения дополнительной информации. По OnChanges ловушка 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 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();
    })


});

Ответ 2

У вас также есть возможность вызвать ловушку 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.

Ответ 3

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