繁体   English   中英

"Angular 2:如何在单元测试时模拟 ChangeDetectorRef"

[英]Angular 2: How to mock ChangeDetectorRef while unit testing

我刚刚开始进行单元测试,我已经能够模拟我自己的服务以及一些 Angular 和 Ionic,但无论我做什么, ChangeDetectorRef<\/code>都保持不变。

我的意思是这是什么魔法?

beforeEach(async(() => 
    TestBed.configureTestingModule({
      declarations: [MyComponent],
      providers: [
        Form, DomController, ToastController, AlertController,
        PopoverController,

        {provide: Platform, useClass: PlatformMock},
        {
          provide: NavParams,
          useValue: new NavParams({data: new PageData().Data})
        },
        {provide: ChangeDetectorRef, useClass: ChangeDetectorRefMock}

      ],
      imports: [
        FormsModule,
        ReactiveFormsModule,
        IonicModule
      ],
    })
    .overrideComponent(MyComponent, {
      set: {
        providers: [
          {provide: ChangeDetectorRef, useClass: ChangeDetectorRefMock},
        ],
        viewProviders: [
          {provide: ChangeDetectorRef, useClass: ChangeDetectorRefMock},
        ]
      }
    })
    .compileComponents()
    .then(() => {
      let fixture = TestBed.createComponent(MyComponent);
      let cmp = fixture.debugElement.componentInstance;

      let cdRef = fixture.debugElement.injector.get(ChangeDetectorRef);

      console.log(cdRef); // logs ChangeDetectorRefMock
      console.log(cmp.cdRef); // logs ChangeDetectorRef , why ??
    })
  ));

2020 年更新

我最初是在 2017 年 5 月写的,这是一个在当时非常有效并且仍然有效的解决方案。

我们无法通过测试台配置 changeDetectorRef 模拟的注入,所以这就是我最近在做的事情:

 it('detects changes', () => {
      // This is a unique instance here, brand new
      const changeDetectorRef = fixture.debugElement.injector.get(ChangeDetectorRef); 
     
      // So, I am spying directly on the prototype.
      const detectChangesSpy = spyOn(changeDetectorRef.constructor.prototype, 'detectChanges');

      component.someMethod(); // Which internally calls the detectChanges.

      expect(detectChangesSpy).toHaveBeenCalled();
    });

那么你不关心私有属性或任何。


万一有人遇到这种情况,这是一种对我很有效的方法:

当您在构造函数中注入 ChangeDetectorRef 实例时:

 constructor(private cdRef: ChangeDetectorRef) { }

您将cdRef作为组件的私有属性之一,这意味着您可以监视组件,存根该属性并让它返回您想要的任何内容。 此外,您可以根据需要断言其调用和参数。

在您的规范文件中,在不提供 ChangeDetectorRef 的情况下调用您的 TestBed,因为它不会提供您提供的内容。 在每个块之前设置相同的组件,因此它会在规范之间重置,就像在此处的文档中所做的那样:

component = fixture.componentInstance;

然后在测试中,直接对属性进行spy

describe('someMethod()', () => {
  it('calls detect changes', () => {
    const spy = spyOn((component as any).cdRef, 'detectChanges');
    component.someMethod();

    expect(spy).toHaveBeenCalled();
  });
});

对于间谍,您可以使用.and.returnValue()并让它返回您需要的任何内容。

注意(component as any)被用作cdRef是一个私有属性。 但是私有在实际编译的javascript中不存在,所以它是可以访问的。

如果您想在运行时以这种方式访问​​私有属性以进行测试,这取决于您。

不确定这是否是新事物,但可以通过夹具访问 changeDetectorRef。

请参阅文档: https : //angular.io/guide/testing#componentfixture-properties

我们在更改检测器模拟方面遇到了同样的问题,这最终成为了解决方案

可能需要指出的一点是,本质上您想在这里测试自己的代码,而不是对变更检测器本身进行单元测试(由 Angular 团队测试)。 在我看来,这是一个很好的指标,表明您应该将对变更检测器的调用提取到本地私有方法(私有,因为它是您不想进行单元测试的东西),例如

private detectChanges(): void {
    this.cdRef.detectChanges();
}

然后,在您的单元测试中,您需要验证您的代码是否实际调用了此函数,从而调用了 ChangeDetectorRef 中的方法。 例如:

it('should call the change detector',
    () => {
        const spyCDR = spyOn((cmp as any).cdRef, 'detectChanges' as any);
        cmp.ngOnInit();
        expect(spyCDR).toHaveBeenCalled();
    }
);

我遇到了完全相同的情况,这是一位高级开发人员向我建议的单元测试的一般最佳实践,他告诉我单元测试实际上迫使您通过这种模式更好地构建代码。 通过建议的重组,您可以确保您的代码可以灵活更改,例如,如果 Angular 更改了它们为我们提供更改检测的方式,那么您只需调整 detectChanges 方法。

对于单元测试,如果您只是为了满足要创建的组件的依赖注入而ChangeDetectorRef ,则可以传入任何值。

对于我的情况,我这样做了:

TestBed.configureTestingModule({
  providers: [
    FormBuilder,
    MyComponent,
    { provide: ChangeDetectorRef, useValue: {} }
  ]
}).compileComponents()
injector = getTestBed()
myComponent = injector.get(MyComponent)

它将成功创建myComponent 只要确保测试执行路径不需要ChangeDetectorRef 如果你这样做,那么用适当的模拟对象替换useValue: {}

就我而言,我只需要使用FormBuilder测试一些表单创建内容。

// component
constructor(private changeDetectorRef: ChangeDetectorRef) {}

public someHandler() {
  this.changeDetectorRef.detectChanges();
}     

// spec
const changeDetectorRef = fixture.componentRef.changeDetectorRef;
jest.spyOn(changeDetectorRef, 'detectChanges');
fixture.detectChanges(); // <--- needed!!

component.someHandler();

expect(changeDetectorRef.detectChanges).toHaveBeenCalled();

暂无
暂无

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

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