在 Angular 2 中测试 OnPush 组件

alt*_*ler 9 testing angular-cli angular

我在使用OnPush变更检测策略测试组件时遇到问题。

测试是这样的

it('should show edit button for featured only for owners', () => {
    let selector = '.edit-button';

    component.isOwner = false;
    fixture.detectChanges();

    expect(fixture.debugElement.query(By.css(selector))).toBeFalsy();

    component.isOwner = true;
    fixture.detectChanges();

    expect(fixture.debugElement.query(By.css(selector))).toBeTruthy();
});
Run Code Online (Sandbox Code Playgroud)

如果我使用Default策略,它会按预期工作,但OnPush更改isOwner为 时不会通过调用detectChanges. 我错过了什么吗?

小智 8

这个问题很容易解决... https://github.com/angular/angular/issues/12313#issuecomment-298697327

TestBed.configureTestingModule({
  declarations: [ MyComponent ] 
})
.overrideComponent(MyComponent, {
  set: {  changeDetection: ChangeDetectionStrategy.Default  }
})
.compileComponents();
Run Code Online (Sandbox Code Playgroud)

请记住,这种方法可能会掩盖一些更改检测问题

学分:马基托斯


Rem*_*emi 8

它不起作用,因为您的装置中的changeDetectorRef与您的组件中的不同。摘自 Angular 中的问题:

“...ComponentRef 上的changeDetectorRef 指向动态创建的组件的根(主机)视图的更改检测器。然后,在主机视图内我们得到了实际的组件视图,但组件视图是 OnPush 因此我们永远不会刷新一下!” -来源

选项A。解决这个问题的一种方法是使用组件注入器来获取真正的changeDetectionRef:

describe('MyComponent', () => {
  let fixture;
  let component;

  beforeEach(() => {
    TestBed.configureTestingModule({ ... }).compileComponents();
    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('does something', () => {
    // set the property here
    component.property = 'something';

    // do a change detection on the real changeDetectionRef
    fixture.componentRef.injector.get(ChangeDetectorRef).detectChanges();

    expect(...).toBe(...);
  });
});
Run Code Online (Sandbox Code Playgroud)

您还可以只使用到 @Input 的初始绑定(它最初触发 OnPush 策略的更改检测):

选项B1:

describe('MyComponent', () => {
  let fixture;
  let component;

  beforeEach(() => {
    TestBed.configureTestingModule({ ... }).compileComponents();
    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;
  });

  it('does something', () => {
    // set the property here
    component.property = 'something';

    // do the first (and only) change detection here
    fixture.detectChanges();

    expect(...).toBe(...);
  });
});
Run Code Online (Sandbox Code Playgroud)

或者例如:

选项B2:

describe('MyComponent', () => {
  let fixture;
  let component;

  it('does something', () => {
    // set the property here
    setup({ property: 'something' });
    expect(...).toBe(...);
  });

  function setup(props: { property? } = {}) {
    TestBed.configureTestingModule({ ... }).compileComponents();
    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;

    Object.getOwnPropertyNames(props).forEach((propertyName) => {
      component[propertyName] = props[propertyName];
    });

    // do the first (and only) change detection here
    fixture.detectChanges();
  }
});
Run Code Online (Sandbox Code Playgroud)


Mar*_*bek 5

您需要告诉 angular 您更改了组件的输入属性。在理想的世界中,您将替换

component.isOwner = false;
fixture.detectChanges();
Run Code Online (Sandbox Code Playgroud)

和

component.isOwner = false;
fixture.changeDetectorRef.markForCheck();
fixture.detectChanges();
Run Code Online (Sandbox Code Playgroud)

不幸的是,这不起作用,因为 angular 存在一个错误(https://github.com/angular/angular/issues/12313)。您可以使用那里描述的解决方法之一。


yur*_*zui 4

如果您查看了 @G\xc3\xbcnter\'s 的答案Angular 2 ChangeDetection 和 ChangeDetectionStrategy.OnPush,那么您可以使用事件处理程序来解决它,例如:

\n\n
const fixture = TestBed.overrideComponent(TestComponent, {set: {host: { "(click)": "dummy" }}}).createComponent(TestComponent);\n// write your test\nfixture.debugElement.triggerEventHandler(\'click\', null);\nfixture.detectChanges();\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是Plunker 示例

\n