Angular 测试 - 无法读取未定义的 属性 'clear'
Angular testing - Cannot read property 'clear' of undefined
我有如下组件
<select #tabSelect (change)="tabLoad($event.target.value)" class="mr-2">
<option value="tab1">First tab</option>
<option value="tab2">Second tab</option>
</select>
<div class="tab-content">
<div class="tab-pane fade show active">
<ng-template #tabContent></ng-template>
</div>
</div>
有两个选项卡,它们调用 tabLoad() 函数并发送参数点击选项卡。
export class DemoComponent implements OnInit {
@ViewChild('tabContent', { read: ViewContainerRef }) entry: ViewContainerRef;
activeTab: any = 'tab1';
constructor(private resolver: ComponentFactoryResolver) { }
ngOnInit() {
this.tabLoad(this.activeTab);
}
tabLoad(page) {
setTimeout(() => {
this.activeTab = page;
this.entry.clear();
if (page == 'tab1') {
const factory = this.resolver.resolveComponentFactory(Tab1Component);
console.log(this.entry);
this.entry.createComponent(factory);
} else if (page == 'tab2') {
const factory = this.resolver.resolveComponentFactory(Tab2Component);
this.entry.createComponent(factory);
}
}, 500);
}
}
在这个 .ts 文件中,我创建了一个名为 entry 的变量,它指向 #tabContent.Tab 内容加载组件取决于哪个页面处于活动状态。
我为此行为编写了一个测试套件,如下所示
fdescribe('DemoComponent', () => {
let component: DemoComponent;
let fixture: ComponentFixture<DemoComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [RouterModule.forRoot([]), SharedModule],
declarations: [Tab1Component, Tab2Component],
}).compileComponents().then(() => {
fixture = TestBed.createComponent(DemoComponent);
component = fixture.componentInstance;
});
}));
it('should set activeTab correctly and clear entry when tabLoad is called', fakeAsync(() => {
component.tabLoad("tab1");
flush();
expect(component.activeTab).toBe('tab1');
}));
});
此测试失败并在我调用 this.entry.clear() 时显示 Cannot read property 'clear' of undefined
; . console.log(this.entry);
也打印未定义。
然后我决定在 .compileComponents().then(() => {})
范围内添加 fixture.detectChanges()
但在 ng 服务一切正常后我转到页面时仍然在相同的 way.But 中失败。
第一个 fixture.detectChanges()
调用 ngOnInit
并且您需要在 fakeAsync
区域中调用它以使 flush
生效,因为 setTimeout
已创建在 ngOnInit
通话中。本质上,计时器需要在 fakeAsync
区域中创建。因此,请尝试将 fixture.detectChanges()
放入测试中(它块)并将其从 .then
.
中删除
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [RouterModule.forRoot([]), SharedModule],
declarations: [Tab1Component, Tab2Component],
}).compileComponents().then(() => {
fixture = TestBed.createComponent(DemoComponent);
component = fixture.componentInstance;
// !! remove fixture.detectChanges() from here
});
}));
it('should set activeTab correctly and clear entry when tabLoad is called', fakeAsync(() => {
// !! add it here
fixture.detectChanges();
component.tabLoad("tab1");
flush();
expect(component.activeTab).toBe('tab1');
}));
就像 Petr
所说的那样,您可以使用 ngAfterViewInit
.
而不是 500ms
试试这个:
export class DemoComponent implements OnInit, AfterViewInit { /* add AfterViewInit */
@ViewChild('tabContent', { read: ViewContainerRef }) entry: ViewContainerRef;
activeTab: any = 'tab1';
constructor(private resolver: ComponentFactoryResolver) { }
ngOnInit() {
}
ngAfterViewInit() {
this.tabLoad(this.activeTab);
}
tabLoad(page) {
this.activeTab = page;
this.entry.clear();
if (page == 'tab1') {
const factory = this.resolver.resolveComponentFactory(Tab1Component);
console.log(this.entry);
this.entry.createComponent(factory);
} else if (page == 'tab2') {
const factory = this.resolver.resolveComponentFactory(Tab2Component);
this.entry.createComponent(factory);
}
}
}
然后我认为您不需要 fakeAsync
和 flush
。
我有如下组件
<select #tabSelect (change)="tabLoad($event.target.value)" class="mr-2">
<option value="tab1">First tab</option>
<option value="tab2">Second tab</option>
</select>
<div class="tab-content">
<div class="tab-pane fade show active">
<ng-template #tabContent></ng-template>
</div>
</div>
有两个选项卡,它们调用 tabLoad() 函数并发送参数点击选项卡。
export class DemoComponent implements OnInit {
@ViewChild('tabContent', { read: ViewContainerRef }) entry: ViewContainerRef;
activeTab: any = 'tab1';
constructor(private resolver: ComponentFactoryResolver) { }
ngOnInit() {
this.tabLoad(this.activeTab);
}
tabLoad(page) {
setTimeout(() => {
this.activeTab = page;
this.entry.clear();
if (page == 'tab1') {
const factory = this.resolver.resolveComponentFactory(Tab1Component);
console.log(this.entry);
this.entry.createComponent(factory);
} else if (page == 'tab2') {
const factory = this.resolver.resolveComponentFactory(Tab2Component);
this.entry.createComponent(factory);
}
}, 500);
}
}
在这个 .ts 文件中,我创建了一个名为 entry 的变量,它指向 #tabContent.Tab 内容加载组件取决于哪个页面处于活动状态。
我为此行为编写了一个测试套件,如下所示
fdescribe('DemoComponent', () => {
let component: DemoComponent;
let fixture: ComponentFixture<DemoComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [RouterModule.forRoot([]), SharedModule],
declarations: [Tab1Component, Tab2Component],
}).compileComponents().then(() => {
fixture = TestBed.createComponent(DemoComponent);
component = fixture.componentInstance;
});
}));
it('should set activeTab correctly and clear entry when tabLoad is called', fakeAsync(() => {
component.tabLoad("tab1");
flush();
expect(component.activeTab).toBe('tab1');
}));
});
此测试失败并在我调用 this.entry.clear() 时显示 Cannot read property 'clear' of undefined
; . console.log(this.entry);
也打印未定义。
然后我决定在 .compileComponents().then(() => {})
范围内添加 fixture.detectChanges()
但在 ng 服务一切正常后我转到页面时仍然在相同的 way.But 中失败。
第一个 fixture.detectChanges()
调用 ngOnInit
并且您需要在 fakeAsync
区域中调用它以使 flush
生效,因为 setTimeout
已创建在 ngOnInit
通话中。本质上,计时器需要在 fakeAsync
区域中创建。因此,请尝试将 fixture.detectChanges()
放入测试中(它块)并将其从 .then
.
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [RouterModule.forRoot([]), SharedModule],
declarations: [Tab1Component, Tab2Component],
}).compileComponents().then(() => {
fixture = TestBed.createComponent(DemoComponent);
component = fixture.componentInstance;
// !! remove fixture.detectChanges() from here
});
}));
it('should set activeTab correctly and clear entry when tabLoad is called', fakeAsync(() => {
// !! add it here
fixture.detectChanges();
component.tabLoad("tab1");
flush();
expect(component.activeTab).toBe('tab1');
}));
就像 Petr
所说的那样,您可以使用 ngAfterViewInit
.
500ms
试试这个:
export class DemoComponent implements OnInit, AfterViewInit { /* add AfterViewInit */
@ViewChild('tabContent', { read: ViewContainerRef }) entry: ViewContainerRef;
activeTab: any = 'tab1';
constructor(private resolver: ComponentFactoryResolver) { }
ngOnInit() {
}
ngAfterViewInit() {
this.tabLoad(this.activeTab);
}
tabLoad(page) {
this.activeTab = page;
this.entry.clear();
if (page == 'tab1') {
const factory = this.resolver.resolveComponentFactory(Tab1Component);
console.log(this.entry);
this.entry.createComponent(factory);
} else if (page == 'tab2') {
const factory = this.resolver.resolveComponentFactory(Tab2Component);
this.entry.createComponent(factory);
}
}
}
然后我认为您不需要 fakeAsync
和 flush
。