我刚开始进行单元测试,我已经能够嘲笑我自己的服务以及一些角度和Ionic,但是不管我做什么,ChangeDetectorRef
都是一样的。
我是说这是哪种巫术?
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 ??
})
));
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 ??
}));
@Component({
...
})
export class MyComponent {
constructor(private cdRef: ChangeDetectorRef){}
ngOnInit() {
// do something
this.cdRef.markForCheck();
}
}
我什么都试过了,async
,fakeAsync
,injector([ChangeDetectorRef], () => {})
。
毫无办法。
发布于 2017-05-30 18:45:14
更新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
作为组件上的私有属性之一,这意味着您可以监视该组件、该属性的存根并让它返回您想要的任何内容。此外,您还可以根据需要断言它的调用和参数。
在您的规范文件中,调用您的TestBed而不提供ChangeDetectorRef,因为它不会提供您所提供的内容。设置同一个beforeEach块的组件,以便在规范之间重置,就像在docs 这里中那样
component = fixture.componentInstance;
然后在测试中,直接监视属性。
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中不存在私有,因此可以访问它。
如果您希望在运行时以这种方式访问测试的私有属性,则由您决定。
发布于 2018-06-21 12:39:45
不确定这是否是一个新事物,但changeDetectorRef可以通过夹具访问。
见docs:https://angular.io/guide/testing#componentfixture-properties
我们在模拟变更检测器时遇到了同样的问题,这就是最终的解决方案。
发布于 2018-03-13 13:40:10
可能需要指出的一点是,这里本质上您希望测试自己的代码,而不是单元测试变更检测器本身(这是由角度团队测试的)。在我看来,这是一个很好的指示,您应该将对变更检测器的调用提取到本地私有方法(私有方法,因为它是您不想单元测试的东西)。
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();
}
);
我也遇到过同样的情况,这是一位高级开发人员向我推荐的单元测试的一般最佳实践,他告诉我,单元测试实际上迫使您按照这种模式来更好地构造代码。通过建议的重组,您确保您的代码能够灵活地进行更改,例如,如果角度改变了它们为我们提供的更改检测方式,那么您只需调整detectChanges方法即可。
https://stackoverflow.com/questions/41421807
复制相似问题