简体   繁体   English

使用组件的@ViewChild {阅读:ElementRef}导致单元测试失败

[英]Using @ViewChild { read: ElementRef } of component causes unit test to fail

In my component I have a child component that looks like this: 在我的组件中,有一个子组件,如下所示:

<child-component #childComponent></childComponent>

In my parent component I then access this child component using @ViewChild and the read parameter to get the ElementRef, and not the component reference. 然后,在父组件中,我使用@ViewChild和read参数访问此子组件以获取ElementRef,而不是组件引用。 I need the ElementRef to ensure I can get some properties from nativeElement that I need. 我需要的ElementRef,以确保我能得到一些属性nativeElement ,我需要。 So it's like this: 就像这样:

export class ParentComponent {
  @ViewChild('childComponent', { read: ElementRef }) public childComponent: ElementRef;
  public position: string;

  // some way down the code
  private someMethod() {
    if (this.childComponent.nativeElement.offsetLeft > 500) {
      this.position = 'left';
    } else {
      this.position = 'right';
    }
  }
}

So this works for the application, however I am writing the tests and mocking the child component, like this: 因此,这适用于该应用程序,但是我正在编写测试并模拟子组件,如下所示:

@Component({
  selector: 'child-component',
  template: ''
})
class ChildComponentMockComponent {
  private nativeElement = {
    get offsetLeft() {
      return 600
    }
  };
}

beforeEach(async(() => TestBed.configureTestingModule({
  imports: [ ... ],
  declarations: [ ParentComponent, ChildComponentMockComponent ],
  providers: [ ... ],
  schemas: [ NO_ERRORS_SCHEMA ]
}).compileComponents()));

it('should have the correct position, based on position of child-component', () => {
  spyOn(component, 'someMethod');
  expect(component.someMethod).toHaveBeenCalled();
  expect(component.position).toBe('left');
});

So the test will compile the component, and use the mocked child component values as the proper value and compute the value of this.position , which is then asserted in the test. 因此,测试将编译组件,并使用this.position子组件值作为适当值,并计算this.position的值,然后在测试this.position其断言。

However, when the { read: ElementRef } parameter is set, the mock gets completely ignored by the TestBed, even though it's being added in the declarations array. 但是,设置了{ read: ElementRef }参数后,即使已将其添加到声明数组中,TestBed也会完全忽略该模拟。 If I remove { read: ElementRef } , the mock is used in the test and it passes. 如果删除{ read: ElementRef } ,则该模拟将在测试中使用并通过。 But then my application doesn't work, as it is now getting the component reference, where the nativeElement property doesn't exist, rather than the element reference. 但是然后我的应用程序不起作用,因为它现在正在获取组件引用,而nativeElement属性不存在,而不是元素引用。

So how do I get the ElementRef in my application and then in my test use the mock component? 那么,如何在应用程序中获取ElementRef,然后在测试中使用模拟组件呢?

I have fixed this by changing the architecture of the app. 我已经通过更改应用程序的体系结构来解决此问题。 The child component now finds it's own offsetLeft property, and then puts it into an output EventEmitter to be picked up the parent component. 现在,子组件将找到其自己的offsetLeft属性,然后将其放入输出EventEmitter中以获取父组件。

export class ChildComponent implements AfterViewInit {
  @Output() offsetPosition: EventEmitter<number> = new EventEmitter<number>();

  constructor(private el: ElementRef) {}

  public ngAfterViewInit() {
    this.offsetPosition.emit(this.el.nativeElement.offsetLeft);
  }
}

export class ParentComponent implements AfterViewInit {
  public childComponentOffset: number;

  public ngAfterViewInit() {
    setTimeout(() => {
      // this.childComponentOffset is available
      // needs to be in setTimeout to prevent ExpressionChangedAfterItHasBeenCheckedError
      // more info: https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4
    }
  }

  public getChildComponentOffset(position: number): void {
    this.childComponentOffset = position;
  }
}

And then in the HTML, you just define the child component with output variable and method: 然后在HTML中,您只需使用输出变量和方法定义子组件:

<child-component (offsetPosition)="getChildComponentOffset($event)"></child-component>

In the test, I then mock ElementRef for the child component and use it as a provider. 在测试中,然后为子组件模拟ElementRef并将其用作提供程序。

const mockElementRef: any = {
  get offsetLeft() {
    return position;
  }
};

beforeEach(async(() => TestBed.configureTestingModule({
  imports: [ ... ],
  declarations: [ ParentComponent ],
  providers: [
    { provide: ElementRef, useValue: mockElementRef }
  ],
  schemas: [ NO_ERRORS_SCHEMA ]
}).compileComponents()));

it('should have the correct position, based on position of child-component', (done) => {
  component.getChildComponentOffset(600);
  setTimeout(() => expect(component.position).toBe('left'));
  done();
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM