【问题标题】:Angular testing - Cannot read property 'clear' of undefined角度测试 - 无法读取未定义的属性“清除”
【发布时间】:2021-06-27 16:14:50
【问题描述】:

我有一个像下面这样的组件

   <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(() =&gt; {}) 范围内添加fixture.detectChanges(),但仍然以同样的方式失败。但是当我在 ng serve 之后转到页面时,一切正常。

【问题讨论】:

  • 要修复当前的测试,你需要在fakeAsync里面添加fixture.detectChanges(),但实际上超时很奇怪,500看起来像一个神奇的数字,为什么不用ngAfterViewInit来代替
  • @PetrAveryanov 因为我需要在点击标签时触发标签加载。如何使用 ngAfterViewInit 来做到这一点?

标签: javascript angular typescript jasmine karma-runner


【解决方案1】:

第一个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);
      }
  }

}

然后我认为你不需要fakeAsyncflush

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-02
    • 1970-01-01
    • 1970-01-01
    • 2018-04-18
    • 1970-01-01
    • 1970-01-01
    • 2019-08-06
    • 1970-01-01
    相关资源
    最近更新 更多