Angular 2:如何在单元测试时模拟 ChangeDetectorRef [英] Angular 2: How to mock ChangeDetectorRef while unit testing
问题描述
我刚刚开始进行单元测试,我已经能够模拟我自己的服务以及一些 Angular 和 Ionic,但无论我做什么 ChangeDetectorRef
都保持不变.
我的意思是这是哪种魔法?
beforeEach(async(() =>TestBed.configureTestingModule({声明:[MyComponent],提供者:[表单、DomController、ToastController、AlertController、弹出控制器,{提供:平台,使用类:PlatformMock},{提供:导航参数,useValue: new NavParams({data: new PageData().Data})},{提供:ChangeDetectorRef,useClass:ChangeDetectorRefMock}],进口:[表单模块,反应形式模块,离子模块],}).overrideComponent(我的组件,{放: {提供者:[{提供:ChangeDetectorRef,useClass:ChangeDetectorRefMock},],视图提供者:[{提供:ChangeDetectorRef,useClass:ChangeDetectorRefMock},]}}).compileComponents().then(() => {让夹具 = TestBed.createComponent(MyComponent);让 cmp = fixture.debugElement.componentInstance;让 cdRef = fixture.debugElement.injector.get(ChangeDetectorRef);控制台.log(cdRef);//记录 ChangeDetectorRefMock控制台.log(cmp.cdRef);//记录 ChangeDetectorRef ,为什么?})));
<小时>
it('无论如何都会失败', async(() => {spyOn(cdRef, 'markForCheck');spyOn(cmp.cdRef, 'markForCheck');cmp.ngOnInit();期望(cdRef.markForCheck).toHaveBeenCalled();//失败,为什么?期望(cmp.cdRef.markForCheck).toHaveBeenCalled();//成功控制台.log(cdRef);//记录 ChangeDetectorRefMock控制台.log(cmp.cdRef);//记录 ChangeDetectorRef ,为什么?}));
<小时>
@Component({...})导出类 MyComponent {构造函数(私有 cdRef:ChangeDetectorRef){}ngOnInit() {//做一点事this.cdRef.markForCheck();}}
我什么都试过了,async
, fakeAsync
, injector([ChangeDetectorRef], () => {})
.p>
没有任何作用.
2020年更新:
我最初是在 2017 年 5 月写的,这是一个在当时效果很好并且仍然有效的解决方案.
我们无法通过测试台配置 changeDetectorRef 模拟的注入,所以这就是我这些天在做的事情:
it('检测变化', () => {//这是这里唯一的实例,全新的常量 changeDetectorRef = fixture.debugElement.injector.get(ChangeDetectorRef);//所以,我直接在原型上进行监视.常量 detectChangesSpy = spyOn(changeDetectorRef.constructor.prototype, 'detectChanges');组件.someMethod();//内部调用detectChanges.期望(detectChangesSpy).toHaveBeenCalled();});
那你就不用关心私有属性什么的了.
如果有人遇到这种情况,这是一种对我来说效果很好的方法:
当您在构造函数中注入 ChangeDetectorRef 实例时:
构造函数(private cdRef: ChangeDetectorRef) { }
您将 cdRef
作为组件的私有属性之一,这意味着您可以监视组件,存根该属性并让它返回您想要的任何内容.此外,您可以根据需要断言其调用和参数.
在您的规范文件中,调用您的 TestBed 而不提供 ChangeDetectorRef,因为它不会提供您提供的内容.在每个块之前设置相同的组件,因此它在规范之间重置,因为它在文档中完成 这里:
component = fixture.componentInstance;
然后在测试中,直接窥探属性
describe('someMethod()', () => {it('调用检测变化', () => {const spy = spyOn((component as any).cdRef, 'detectChanges');组件.someMethod();期望(间谍).toHaveBeenCalled();});});
有了间谍,你可以使用 .and.returnValue()
并让它返回你需要的任何东西.
注意 (component as any)
被用作 cdRef
是一个私有属性.但是私有在实际编译的javascript中不存在,所以可以访问.
如果您想在运行时以这种方式访问私有属性以进行测试,这取决于您.
I have just started with Unit-Testing, and I have been able to mock my own services and some of Angular and Ionic as well, but no matter what I do ChangeDetectorRef
stays the same.
I mean which kind of sorcery is this?
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();
}
}
I have tried everything , async
, fakeAsync
, injector([ChangeDetectorRef], () => {})
.
Nothing works.
Update 2020:
I wrote this originally in May 2017, it's a solution that worked great at the time and still works.
We can't configure the injection of a changeDetectorRef mock through the test bed, so this is what I am doing these days:
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();
});
Then you don't care about private attributes or any.
In case anyone runs into this, this is one way that has worked well for me:
As you are injecting the ChangeDetectorRef instance in your constructor:
constructor(private cdRef: ChangeDetectorRef) { }
You have that cdRef
as one of the private attributes on the component, which means you can spy on the component, stub that attribute and have it return whatever you want. Also, you can assert its calls and parameters, as needed.
In your spec file, call your TestBed without providing the ChangeDetectorRef as it won't provide what you give it. Set the component that same beforeEach block, so it is reset between specs as it is done in the docs here:
component = fixture.componentInstance;
Then in the tests, spy directly on the attribute
describe('someMethod()', () => {
it('calls detect changes', () => {
const spy = spyOn((component as any).cdRef, 'detectChanges');
component.someMethod();
expect(spy).toHaveBeenCalled();
});
});
With the spy you can use .and.returnValue()
and have it return whatever you need.
Notice that (component as any)
is used as cdRef
is a private attribute. But private doesn't exist in the actual compiled javascript so it is accessible.
It is up to you if you want to access private attributes at runtime that way for your tests.
这篇关于Angular 2:如何在单元测试时模拟 ChangeDetectorRef的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!