简体   繁体   English

用 Jasmine 监视 Observable 订阅的结果

[英]Spy on the result of an Observable Subscription with Jasmine

I am Jasmine unit testing an angular component, which uses Observables.我是 Jasmine 单元测试一个使用 Observables 的角度组件。 My component has this lifecycle hook that I am testing:我的组件有我正在测试的这个生命周期钩子:

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

I have a test that ensures that getCellOEE has been called, but now I want to check that updateChart is called when the observable resolves with a new value.我有一个测试来确保 getCellOEE 已被调用,但现在我想检查当 observable 使用新值解析时是否调用了 updateChart。 This is what I have so far:这是我到目前为止:

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();
  });
});

However, I get the error:但是,我收到错误:

chrome 56.0.2924 (Windows 10 0.0.0) Oee24Component should update chart on new data FAILED chrome 56.0.2924 (Windows 10 0.0.0) Oee24Component 应该更新新数据图表失败

Expected spy updateChart to have been called.预期间谍 updateChart 已被调用。

Presumably this is a timing issue because the observable hasn't necessarily resolved when the test checks?大概这是一个时间问题,因为当测试检查时,observable 不一定解决? If that is the case, how do I set this up correctly?如果是这种情况,我该如何正确设置?

Update:更新:

Here is my component:这是我的组件:

@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);
  }
}
 

Did you ever come up with a solution?你有没有想过解决办法? What about using the jasmine-marbles package and the complete event?使用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');
        }
    });
});

Not sure if this is the best way to do it, but I've seen it working on a project I'm working on.不确定这是否是最好的方法,但我已经看到它在我正在从事的项目中工作。 The approach is basically get a reference to the callback function provided to the subscribe method, and call it manually to simulate the observer emitting a value:该方法基本上是获取对提供给 subscribe 方法的回调函数的引用,并手动调用它以模拟观察者发出值:

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();
  });

Instead of代替

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

You could try你可以试试

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

fixture.detectChanges triggers ngOnInit . fixture.detectChanges触发器ngOnInit so, no need to call ngOnInit manually if fixture.detectChanges was executed.所以,没有必要要求ngOnInit如果手动fixture.detectChanges被执行。

It's a bad practice to check if the method was called . 检查方法是否被调用是一种不好的做法。 Instead, it is more reliable to check the expected results of the code execution.相反,检查代码执行的预期结果更可靠。

There is no need in the line spyOn(service, 'getCellOEE').and.returnValue({ subscribe: () => { } });spyOn(service, 'getCellOEE').and.returnValue({ subscribe: () => { } });行中spyOn(service, 'getCellOEE').and.returnValue({ subscribe: () => { } }); because fakeCellService already mocks properly the service.因为fakeCellService已经正确地fakeCellService了该服务。

The tested code is async, so we need to wait till it would be executed.测试的代码是异步的,所以我们需要等到它被执行。 await fixture.whenStable(); does exactly this.正是这样做的。

So, the resulting test:所以,结果测试:

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);
  });
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM