使用组件的@ViewChild {read:ElementRef}会导致单元测试失败 [英] Using @ViewChild { read: ElementRef } of component causes unit test to fail

查看:134
本文介绍了使用组件的@ViewChild {read:ElementRef}会导致单元测试失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的组件中,我有一个子组件,如下所示:

In my component I have a child component that looks like this:

<child-component #childComponent></childComponent>

在我的父组件中,我然后使用@ViewChild和访问此子组件读取参数以获取ElementRef,而不是组件引用。我需要ElementRef来确保我可以从我需要的 nativeElement 中获取一些属性。所以它是这样的:

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. I need the ElementRef to ensure I can get some properties from nativeElement that I need. 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');
});

因此测试将编译组件,并使用模拟的子组件值作为正确的值并计算 this.position 的值,然后在测试中断言。

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.

然而,当<$ c时$ c> {read:ElementRef} 参数设置,模拟被TestBed完全忽略,即使它被添加到声明数组中。如果我删除 {read:ElementRef} ,则在测试中使用mock并传递。但是我的应用程序不起作用,因为现在它正在获取组件引用,其中 nativeElement 属性不存在,而不是元素引用。

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. If I remove { read: ElementRef }, the mock is used in the test and it passes. 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.

那么如何在我的应用程序中获取ElementRef然后在我的测试中使用模拟组件?

So how do I get the ElementRef in my application and then in my test use the mock component?

推荐答案

我已经通过改变应用程序的架构来解决这个问题。子组件现在找到它自己的offsetLeft属性,然后将它放入一个输出EventEmitter以获取父组件。

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.

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;
  }
}

然后在HTML中,您只需使用输出变量和方法定义子组件:

And then in the HTML, you just define the child component with output variable and method:

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

在测试中,我然后模拟子组件的ElementRef并将其用作提供者。

In the test, I then mock ElementRef for the child component and use it as a provider.

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

这篇关于使用组件的@ViewChild {read:ElementRef}会导致单元测试失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆