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

如何设置测试中的TestComponent.value以便调用ngOnChanges

猜我参加聚会有点晚了,但是这可能对将来的某个人有用。

自从 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),
});

我们正在使用此实用程序函数:

import {OnChanges, SimpleChange, SimpleChanges} from '@angular/core';
export function updateComponentInputs<T extends OnChanges>(
    component: T,
    changes: Partial<T>
) {
    const simpleChanges: SimpleChanges = {};
    Object.keys(changes).forEach(changeKey => {
        component[changeKey] = changes[changeKey];
        simpleChanges[changeKey] = new SimpleChange(
            null,
            changes[changeKey],
            false
        );
    });
    component.ngOnChanges(simpleChanges);
}

你会像这样使用它:

testComponent.updateComponentInputs({value: 'test'});

这会将testComponent.value设置为"test",并使用适当的更改事件调用ngOnChanges。

相关内容

  • 没有找到相关文章

最新更新