Angular 单元测试无法读取未定义的属性

Angular Unit test Cannot read properties of undefined

我正在尝试对一个组件进行单元测试, 组件和页面正在导入表单 @bloomreach/spa-sdk

  export class ThinComponent implements OnInit {
  @Input() component!: BrComponent;
  componentModels: any;
   page: Page;
  
  constructor() {}

  ngOnInit() {
    this.componentModels = this.component.getModels();  
  }

 getContents(contentRef) {
    if (contentRef && contentRef.$ref) {
      this.content = this.page?.getContent(contentRef).getData();
    }
  }
}

我的单元测试像

  it('should create', () => {
     spyOn(component.component, 'getModels').and.returnValue({foo: 'bar'});
     fixture.detectChanges();
     expect(component).toBeTruthy();
  });

抛出和错误TypeError: Cannot read properties of undefined (reading 'getModels')

Ruslan Lekhman 是对的,您需要初始化 component 输入,我认为 spyOn 不适合它。 spyOn 仅适用于 public 方法。

试试这个:

it('should create', () => {
     component.component = { getModels: () => ({ foo: 'bar' }) };
     fixture.detectChanges();
     expect(component).toBeTruthy();
  });

编辑:

尝试将 mockPage 更改为这样的内容。

const getDataSpy = jasmine.createSpy('getData');
mockPage = { getContent: () => ({ getData: getDataSpy }) };
....
expect(getDataSpy).and.callFake(() => { return 'test'; });

编辑

Component.ts

import { Component, Input, VERSION } from '@angular/core';

import { Component as BrComponent, Page } from '@bloomreach/spa-sdk';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  name = 'Angular ' + VERSION.major;
  @Input() component!: BrComponent;
  page: Page;
  componentModels: any;
  public content: any;
  public image: string;
  public inViewport = false;
  constructor() {}
  get configuration() {
    return this.component.getParameters();
  }
  ngOnInit() {
    this.componentModels = this.component?.getModels();
    if (this.componentModels) {
      this.getContents(this.componentModels.document);
    }
  }
  getContents(contentRef) {
    if (contentRef && contentRef.$ref) {
      this.content = this.page?.getContent(contentRef).getData();
      if (this.content) {
        if (this.content.image) {
          this.image = this.getImageUrl(this.content.image);
        }
      }
    }
  }

  getImageUrl($ref): string {
    return this.page.getContent($ref).getUrl();
  }
  onVisible(event) {
    this.inViewport = true;
  }
}

Component.spec.ts

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

import { AppComponent } from './AppComponent';
import { Component as BrComponent, Page } from '@bloomreach/spa-sdk';

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

  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        declarations: [AppComponent],
      }).compileComponents();
    })
  );

  beforeEach(() => {
    fixture = TestBed.createComponent(AppComponent);
    component = fixture.componentInstance;
    component.component = {
      getModels: () => ({ document: { $ref: {} } }),
      getParameters: () => ({}),
    };
    component.page = {
      getContent: () => ({
        getData: () => ({ image: 'abc' }),
        getUrl: () => 'xyz',
      }),
    };
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should test ngOninit', () => {
    spyOn(component, 'getContents').and.callFake(() => null);
    component.ngOnInit();
    expect(component.getContents).toHaveBeenCalled();
  });
  it('should test getContents', () => {
    component.getContents(component.componentModels.document);
    expect(component.image).toBe('xyz');
  });
});