首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >角度2:如何在单元测试中模拟ChangeDetectorRef

角度2:如何在单元测试中模拟ChangeDetectorRef
EN

Stack Overflow用户
提问于 2017-01-02 05:34:22
回答 5查看 20.8K关注 0票数 32

我刚开始进行单元测试,我已经能够嘲笑我自己的服务以及一些角度和Ionic,但是不管我做什么,ChangeDetectorRef都是一样的。

我是说这是哪种巫术?

代码语言:javascript
运行
复制
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 ??
    })
  ));
代码语言:javascript
运行
复制
 it('fails no matter what', async(() => {
    spyOn(cdRef, 'markForCheck');
    spyOn(cmp.cdRef, 'markForCheck');

    cmp.ngOnInit();

    expect(cdRef.markForCheck).toHaveBeenCalled();  // fail, why ??
    expect(cmp.cdRef.markForCheck).toHaveBeenCalled(); // success

    console.log(cdRef); // logs ChangeDetectorRefMock
    console.log(cmp.cdRef); // logs ChangeDetectorRef , why ??
  }));
代码语言:javascript
运行
复制
@Component({
  ...
})
export class MyComponent {
 constructor(private cdRef: ChangeDetectorRef){}

 ngOnInit() {
   // do something
   this.cdRef.markForCheck();
 }
}

我什么都试过了,asyncfakeAsyncinjector([ChangeDetectorRef], () => {})

毫无办法。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2017-05-30 18:45:14

更新2020

我最初是在2017年5月写这篇文章的,这是一种当时效果很好的解决方案,至今仍然有效。

我们无法通过测试床配置changeDetectorRef模拟的注入,所以这几天我就是这样做的:

代码语言:javascript
运行
复制
 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实例时:

代码语言:javascript
运行
复制
 constructor(private cdRef: ChangeDetectorRef) { }

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

在您的规范文件中,调用您的TestBed而不提供ChangeDetectorRef,因为它不会提供您所提供的内容。设置同一个beforeEach块的组件,以便在规范之间重置,就像在docs 这里中那样

代码语言:javascript
运行
复制
component = fixture.componentInstance;

然后在测试中,直接监视属性。

代码语言:javascript
运行
复制
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中不存在私有,因此可以访问它。

如果您希望在运行时以这种方式访问测试的私有属性,则由您决定。

票数 38
EN

Stack Overflow用户

发布于 2018-06-21 12:39:45

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

见docs:https://angular.io/guide/testing#componentfixture-properties

我们在模拟变更检测器时遇到了同样的问题,这就是最终的解决方案。

票数 7
EN

Stack Overflow用户

发布于 2018-03-13 13:40:10

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

代码语言:javascript
运行
复制
private detectChanges(): void {
    this.cdRef.detectChanges();
}

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

代码语言:javascript
运行
复制
it('should call the change detector',
    () => {
        const spyCDR = spyOn((cmp as any).cdRef, 'detectChanges' as any);
        cmp.ngOnInit();
        expect(spyCDR).toHaveBeenCalled();
    }
);

我也遇到过同样的情况,这是一位高级开发人员向我推荐的单元测试的一般最佳实践,他告诉我,单元测试实际上迫使您按照这种模式来更好地构造代码。通过建议的重组,您确保您的代码能够灵活地进行更改,例如,如果角度改变了它们为我们提供的更改检测方式,那么您只需调整detectChanges方法即可。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41421807

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档