在Angular 2中测试ngOnChanges生命周期钩子

use*_*982 25 karma-jasmine angular2-testing angular2-components angular

鉴于以下代码,我尝试测试ngOnChangesAngular2 的生命周期钩子:

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));
    });
});
Run Code Online (Sandbox Code Playgroud)

不幸的是,测试失败的消息Expected spy ngOnChanges to have been called.我知道我可以在这个例子中检查HTML元素的内容,但我有一些代码需要在ngOnChanes生命周期钩子内部进行测试,所以这对我来说不是解决方案.我也不想testComponent.ngOnChanges({someMockData});直接打电话给考试.

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

Kir*_*ndi 53

猜猜我有点迟到了,不过这可能对将来有些人有用.

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


});
Run Code Online (Sandbox Code Playgroud)

  • 如果你想要特定的改变:component.ngOnChanges({'key':new SimpleChange(false,true)}) (4认同)
  • 今天还有其他解决方案吗?我不想为我的所有组件编写 TestComponent...:( (3认同)
  • 好吧,到目前为止,我还没找到一个:(.实际上在这个主题上提出了一个问题:https://github.com/angular/angular/issues/6235.阅读pkozlowski-opensource对此的评论 (2认同)

s-f*_*s-f 15

您还可以选择手动调用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);
Run Code Online (Sandbox Code Playgroud)


The*_*inn 8

在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)});
Run Code Online (Sandbox Code Playgroud)