【问题标题】:Testing observable object angular 2 karma测试可观察对象 Angular 2 业力
【发布时间】:2023-03-29 16:53:01
【问题描述】:

我正在使用 Karma 为 Angular 2 编写单元测试用例,我遇到了一个函数,我在其中运行测试以进行下一行

expect(component.subscribeToEvents()).toBeTruthy();

我查看了我的覆盖率代码,测试文件中的行似乎没有覆盖订阅中的任何内容。我曾尝试使用 MockBackend 在服务函数中模拟 api 调用,但我不确定如何对订阅的对象进行模拟,有人可以帮我吗?

下面是test.component.ts

subscribeToEvents() {
this.subscription = this.czData.$selectedColorZone
  .subscribe(items => {
    this.resourceLoading = true;
    if (!this.resourceData || (this.resourceData && this.resourceData.length === 0)) {
      this.settings.layout.flypanel.display = false;
      this.getAllResources(this.pagination.start, this.pagination.size);
    }
    else {
      this.pagination.start = 1;
      this.pagination.end = this.pagination.size;
      this.getAllResources(1, this.pagination.size);
      this.settings.layout.flypanel.display = true;
    }
  });
return true;

}

覆盖代码截图

【问题讨论】:

    标签: unit-testing angular karma-jasmine angular2-services karma-coverage


    【解决方案1】:

    考虑如何测试 Angular 输出,因为它们在测试期间被订阅:https://angular.io/guide/testing#clicking

    it('should raise selected event when clicked (triggerEventHandler)', () => {
      let selected: Hero;
      comp.selected.subscribe((hero: Hero) => selectedHero = hero);
    
      heroDe.triggerEventHandler('click', null);
      expect(selectedHero).toBe(expectedHero);
    });
    

    那就试试吧:

    const expectedItem = {}; // mock the expected result from 'subscribeToEvents'
    
    it('should raise selected event when clicked (triggerEventHandler)', () => {
      let selectedItem: any; // change to expected type
      component.subscribeToEvents.subscribe((item: any) => selectedItem = item);
      // fixture.detectChanges(); // trigger change detection if necessary here, depending on what triggers 'subscribeToEvents'
      expect(selectedItem).toBe(expectedItem);
    });
    

    【讨论】:

      【解决方案2】:

      您不能这样做,因为订阅是异步解决的。所以同步测试在异步任务解决之前就完成了。

      如果您想要的只是覆盖范围,您可以进行测试async。这将导致 Angular 测试区等到异步任务解决后再完成测试

      import { async } from '@angular/core/testing';
      
      it('..', async(() => {
        component.subscribeToEvents();
      }))
      

      你不能在这里期待任何东西,因为当任务解决时没有回调挂钩。所以这真的是一个毫无意义的测试。它会给你覆盖,但你实际上并没有测试任何东西。例如,您可能想测试在解决订阅时是否设置了变量。

      根据提供的代码,我要做的只是模拟服务,并使其同步。你怎么能那样做?我们可以使模拟类似于

      class CzDataSub {
        items: any = [];
      
        $selectedColorZone = {
          subscribe: (callback: Function) => {
            callback(this.items);
          }
        }
      }
      

      那就在测试中配置一下

      let czData: CzDataStub;
      
      beforeEach(() => {
        czData = new CzDataStub();
        TestBed.configureTestingModule({
          providers: [
            { provide: CzData, useValue: czData }
          ]
        })
      })
      

      现在在您的测试中,您无需将其设为async,您只需在模拟上设置items 属性即可提供您想要的任何值,订阅者将获得它

      it('..', () => {
        czData.items = something;
        component.subscribeToEvents();
        expect(component.settings.layout.flypanel.display).toBe(false);
      })
      

      更新

      我想我在写这篇文章的时候已经半睡半醒了。以上说法有一项不正确

      你不能在这里期待任何事情,因为当任务解决时没有回调钩子。

      这并不完全正确。这就是fixture.whenStable() 的用途。例如,如果这是您的服务

      class CzData {
        _value = new Subject<>();
      
        $selectedColorZone = this._value.asObservable();
      
        setValue(value) {
          this._value.next(value);
        }
      }
      

      那么这就是你将如何进行测试

      let czData: CzData;
      let fixture: ComponentFixture<YourComponent>;
      let component: YourComponent;
      
      beforeEach(() => {
        TestBed.configureTestingModule({
          providers: [ CzData ],
          declarations: [ YourComponent ]
        });
        fixture = TestBed.createComponent(YourComponent);
        component = fixture.componentInstance;
        czData = TestBed.get(czData);
      })
      
      it('..', async(() => {
        component.subscribeToEvents();
        czData.setValue(somevalue);
        fixture.whenStable().then(() => {
          expect(component.settings.layout.flypanel.display).toBe(false);
        })
      }))
      

      我们使用fixture.whenStable() 来等待异步任务完成。

      这并不是说使用模拟是错误的。很多时候,使用模拟将是要走的路。我只是想更正我的说法,并说明如何做到这一点。

      【讨论】:

      • 我尝试了上述方法,但由于 { provide: CzData: useClass: czData } 导致我的测试失败,知道为什么吗?
      • 抱歉,它应该是 useValue 而不是 useClass。并确保provide 实际上是您的班级。我看不到你的班级的名字,所以我只是假设它是这样的。它应该是逗号而不是冒号。我认为这就是错误。查看更新的代码
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-06-10
      • 2018-04-11
      • 2017-05-17
      • 1970-01-01
      • 1970-01-01
      • 2017-06-11
      • 2019-05-18
      相关资源
      最近更新 更多