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

问题描述 投票:0回答:5

鉴于以下代码,我尝试测试 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 karma-jasmine angular2-components angular2-testing
5个回答
77
投票

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

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



39
投票
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
装饰器是否已正确设置。
    


29
投票
ngOnChanges()

,您必须手动进行调用(如上所述),只需匹配 SimpleChange() 的

new 调用签名
: 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), });



3
投票

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。


0
投票
setInput()

。这样,Angular 就会使用正确的参数调用 ngOnChanges:

describe('TestComponent', () => {
    let component: TestComponent;
    let fixture: ComponentFixture<TestComponent>;

    beforeEach(() => {
        // configure TestBed, etc.
        // ...

        fixture = TestBed.createComponent(TestComponent);
        component = fixture.componentInstance;
    });

    it('should call ngOnChanges', () => {
        spyOn(component, 'ngOnChanges').and.callThrough();

        fixture.componentRef.setInput('value', 'Test');
        fixture.detectChanges();

        expect(component.ngOnChanges).toHaveBeenCalled();
    });
});

	
© www.soinside.com 2019 - 2024. All rights reserved.