【问题标题】:Spy on the result of an Observable Subscription with Jasmine使用 Jasmine 监视 Observable 订阅的结果
【发布时间】:2017-03-31 08:05:27
【问题描述】:

我是 Jasmine 单元测试一个角度组件,它使用 Observables。我的组件有我正在测试的这个生命周期钩子:

ngOnInit() {
  this.dataService.getCellOEE(this.cell).subscribe(value => this.updateChart(value));
}

我有一个测试可以确保 getCellOEE 已被调用,但现在我想检查当 observable 使用新值解析时是否调用了 updateChart。这是我目前所拥有的:

let fakeCellService = {
  getCellOEE: function (value): Observable<Array<IOee>> {
    return Observable.of([{ time: moment(), val: 67 }, { time: moment(), val: 78 }]);
  }
};

describe('Oee24Component', () => {
  let component: Oee24Component;
  let service: CellService;
  let injector: Injector;
  let fixture: ComponentFixture<Oee24Component>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [Oee24Component],
      providers: [{ provide: CellService, useValue: fakeCellService }]
    })
      .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(Oee24Component);
    component = fixture.componentInstance;
    injector = getTestBed();
    service = injector.get(CellService)
    fixture.detectChanges();
    spyOn(service, 'getCellOEE').and.returnValue({ subscribe: () => { } });
    spyOn(component, 'updateChart');
  });

  it('should get cell oee on init', () => {
    component.ngOnInit();
    expect(service.getCellOEE).toHaveBeenCalled();
  });

  it('should update chart on new data', () => {
    component.ngOnInit();
    expect(component.updateChart).toHaveBeenCalled();
  });
});

但是,我得到了错误:

chrome 56.0.2924 (Windows 10 0.0.0) Oee24Component 应该在新数据上更新图表失败

预期的间谍 updateChart 已被调用。

大概这是一个时间问题,因为在测试检查时观察到的不一定解决?如果是这种情况,我该如何正确设置?

更新:

这是我的组件:

@Component({
  selector: 'app-oee24',
  templateUrl: './oee24.component.html',
  styleUrls: ['./oee24.component.css']
})
export class Oee24Component implements OnInit {
  public barChartData: any[] = [{ data: [], label: 'OEE' }];

  constructor(public dataService: CellService) { }

  ngOnInit() {
    this.dataService.getCellOEE(this.cell).subscribe(value => this.updateChart(value));
  }

  updateChart(data: Array<IOee>) {
    this.barChartData[0].data = data.map(val => val.val);
  }
}
 

【问题讨论】:

  • 你在监视你的服务,并让它返回一个假的 observable,它的 subscribe() 方法什么都不做(因此永远不会调用传递给 subscribe() 的回调)。只是不要那样做。
  • @JBNizet 我该怎么办?
  • 我不知道监视.subscribe 是如何让你得到你想要的。您已经在监视要测试的函数实际上正在被调用,Observable.of 为它提供了数据。您不需要明确检查订阅是否发生;如果没有,该数据将不会到达updateChart。相反,请尝试检查 what updateChart 的调用方式,以便根据已构建的 observable 检查它是否是正确的数据。
  • @jonrsharpe 我正在尝试检查 updateChart 是否被调用,但我同意看到它被调用的内容会很方便,但是我上面显示的测试仍然失败?这就是为什么我想我需要查看订阅以尝试解决任何时间问题?
  • 分享你的组件代码^^

标签: unit-testing angular typescript jasmine karma-jasmine


【解决方案1】:

你有没有想出一个解决方案?使用jasmine-marbles 包和complete 事件怎么样?

it('should update chart on new data', () => {
    const obs$ = cold('--a-|');
    spyOn(service, 'getCellOEE').and.returnValue(obs$);
    component.ngOnInit(); 
    obs$.subscribe({
        complete: () => {
            expect(component.updateChart).toHaveBeenCalledWith('a');
        }
    });
});

【讨论】:

  • 这里const obs$ = cold('--a-|');这个方法是什么?
【解决方案2】:

不确定这是否是最好的方法,但我已经看到它在我正在从事的项目中发挥作用。该方法基本上是获取订阅方法提供的回调函数的引用,并手动调用它以模拟观察者发出值:

it('should update chart on new data', () => {
    component.ngOnInit();

    // this is your mocked observable
    const obsObject = service.getCellOEE.calls.mostRecent().returnValue;

    // expect(obsObject.subscribe).toHaveBeenCalled() should pass

    // get the subscribe callback function you provided in your component code
    const subscribeCb = obsObject.subscribe.calls.mostRecent().args[0];

    // now manually call that callback, you can provide an argument here to mock the "value" returned by the service
    subscribeCb(); 

    expect(component.updateChart).toHaveBeenCalled();
  });

【讨论】:

    【解决方案3】:

    代替

    spyOn(service, 'getCellOEE').and.returnValue({ subscribe: () => { } });
    

    你可以试试

    spyOn(service, 'getCellOEE').and.returnValue( {subscribe: (callback) => callback()});
    

    【讨论】:

      【解决方案4】:

      fixture.detectChanges 触发 ngOnInit。因此,如果 fixture.detectChanges 被执行,则无需手动调用 ngOnInit

      to check if the method was called 是一种不好的做法。相反,检查代码执行的预期结果更可靠。

      spyOn(service, 'getCellOEE').and.returnValue({ subscribe: () =&gt; { } }); 行中不需要,因为fakeCellService 已经正确地模拟了服务。

      测试的代码是异步的,所以我们需要等到它被执行。 await fixture.whenStable(); 正是这样做的。

      所以,结果测试:

      const fakeData = [{ time: moment(), val: 67 }, { time: moment(), val: 78 }];
      const expectedChartData = [67, 78];
      const fakeCellService = {
        getCellOEE: function (value): Observable<Array<IOee>> {
          return Observable.of(fakeData);
        }
      };
      
      describe('Oee24Component', () => {
        let component: Oee24Component;
        let service: CellService;
        let injector: Injector;
        let fixture: ComponentFixture<Oee24Component>;
      
        beforeEach(async(() => {
          TestBed.configureTestingModule({
            declarations: [Oee24Component],
            providers: [{ provide: CellService, useValue: fakeCellService }]
          })
            .compileComponents();
        }));
      
        beforeEach(async () => {
          fixture = TestBed.createComponent(Oee24Component);
          component = fixture.componentInstance;
          fixture.detectChanges();
          await fixture.whenStable();
        });
      
        it('maps and saves value from the CellService.getCellOEE to barChartData[0].data when initialized', () => {
          expect(component.barChartData[0].data).toEqual(expectedChartData);
        });
      });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-02-10
        • 2021-09-02
        • 1970-01-01
        • 1970-01-01
        • 2018-12-27
        • 2017-09-03
        相关资源
        最近更新 更多