在Jasmine测试中AfterViewInit的生命周期钩子

时间:2017-07-11 15:23:27

标签: angular typescript jasmine karma-jasmine

我对生命周期钩子与Jasmine测试的关系感到困惑。 LifeCycle Angular doc没有提到测试https://angular.io/guide/lifecycle-hooks。测试文档仅提到OnChange https://angular.io/guide/testing。 我有一个示例组件如下:

import { Component, OnInit, AfterViewInit, OnDestroy, ElementRef } from '@angular/core';
...
@Component({
  selector: 'app-prod-category-detail',
  templateUrl: './prod-category-detail.component.html',
  styleUrls: ['./prod-category-detail.component.css']
})
//
export class ProdCategoryDetailComponent implements OnInit, AfterViewInit, OnDestroy {
    ...
    nav: HTMLSelectElement;
    //
    constructor(
        ...
        private _elementRef: ElementRef ) { }
    ...
    ngAfterViewInit() {
        console.log( 'ProdCategoryDetailComponent: ngAfterViewInit' );
        this.nav = this._elementRef.nativeElement.querySelector('#nav');
    }
    ...
}

请注意,这是一个具有最新下载量的Angular CLI应用程序。在Karma中,我看不到控制台日志,因此永远不会设置 nav 。我目前在我的规范中调用它如下:

beforeEach(() => {
  fixture = TestBed.createComponent(ProdCategoryDetailComponent);
  sut = fixture.componentInstance;
  sut.ngAfterViewInit( );
  fixture.detectChanges( );
});

这是处理此问题的正确方法吗?

对于舒森来说这是从前一段时间开始的,我已经有一段时间没看过这个了。希望它会有所帮助。注意,我使用的是Primeface primeng库:

describe('ProdCategoryDetailComponent', () => {
  let sut: ProdCategoryDetailComponent;
  let fixture: ComponentFixture< ProdCategoryDetailComponent >;
  let alertService: AlertsService;
  let prodCatService: ProdCategoryServiceMock;
  let confirmService: ConfirmationServiceMock;
  let elementRef: MockElementRef;
  //
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        FormsModule,
        ButtonModule,
        BrowserAnimationsModule
      ],
      declarations: [
        ProdCategoryDetailComponent,
        AlertsComponent,
        ConfirmDialog
      ],
      providers: [
        AlertsService,
        { provide: ProdCategoryService, useClass: ProdCategoryServiceMock },
        { provide: MockBackend, useClass: MockBackend },
        { provide: BaseRequestOptions, useClass: BaseRequestOptions },
        { provide: ConfirmationService, useClass: ConfirmationServiceMock },
        { provide: ElementRef, useClass: MockElementRef }
      ]
    })
    .compileComponents();
  }));
  //
  beforeEach(inject([AlertsService, ProdCategoryService,
      ConfirmationService, ElementRef],
        (srvc: AlertsService, pcsm: ProdCategoryServiceMock,
        cs: ConfirmationServiceMock, er: MockElementRef) => {
    alertService = srvc;
    prodCatService = pcsm;
    confirmService = cs;
    elementRef = er;
  }));
  //
  beforeEach(() => {
    fixture = TestBed.createComponent(ProdCategoryDetailComponent);
    sut = fixture.componentInstance;
    sut.ngAfterViewInit( );
    fixture.detectChanges( );
  });
  //

2 个答案:

答案 0 :(得分:2)

我经常在必要时直接调用每个规范的生命周期钩子。这很有效。 因为这可以在调用ngAfterViewInit()ngOnInit()之前灵活地处理任何数据。

我也看到过几个角度库测试规范以同样的方式使用它。例如,检查此videogular规范文件。因此手动调用这些方法没有坏处。

此处也复制相同的代码,以避免将来破坏链接。

it('Should hide controls after view init', () => {
        spyOn(controls, 'hide').and.callFake(() => {});

        controls.vgAutohide = true;

        controls.ngAfterViewInit();

        expect(controls.hide).toHaveBeenCalled();
});

答案 1 :(得分:0)

我们不能直接从规范中调用生命周期挂钩,但可以调用自定义方法。因为要调用生命周期书,我们需要使用Fixture创建组件的实例。

示例:在我的示例中,我需要将padding-left和right设置为0,因为bootstrap默认情况下会左右填充一些,因此我需要将其删除。

HTML文件-app.component.ts

import { Component, ViewChild, ElementRef, Renderer2 } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  @ViewChild('navCol', { static: true }) navCol: ElementRef;

  constructor(private renderer: Renderer2) {  }

  ngAfterViewInit() {
    this.renderer.setStyle(this.navCol.nativeElement, 'padding-left', '0px');
    this.renderer.setStyle(this.navCol.nativeElement, 'padding-right', '0px');
  }
}

app.component.spec.ts

import { TestBed, async } from '@angular/core/testing';

describe('AppComponent', () => {
  it('should load page and remove padding', () => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.componentInstance.ngAfterViewInit();
    const styles = window.getComputedStyle(fixture.nativeElement);

    expect(styles.paddingLeft).toBe('0px');
    expect(styles.paddingRight).toBe('0px');
  });
});