JB17
JB17

Reputation: 459

Jasmine marble testing observable with ngIf async pipe

I want to test an Observable with jasmine-marble testing, but unfortunately I can't figure out how to trigger change detection for ngIf, which should render the component.

This is a simplified version of my class:

export class MyComponent implements OnInit {
  data$: Observable<{data: any[]}>;

  constructor(private baseService: BaseService) { }

  ngOnInit(): void {
    this.data$ = this.baseService.get(endpoint);
  }
}

And my html file:

<custom-component *ngIf="data$ | async as value" [data]="value.data">
    ...
</custom-component>

This is my current test, which fails:

it ('should display custom component', fakeAsync(() => {
    const expected = cold('a|',  {a: {data: [{id: 1}]}});
    baseServiceStub.get.and.returnValue(expected);
    component.ngOnInit();
    fixture.detectChanges();
    tick();
    expect(component.data$).toBeObservable(expected); // this passes and the observable also holds the correct value
    expect(baseService.get).toHaveBeenCalledWith(endpoint); // this passes aswell
    component.data$.subscribe(val => {
      console.log(val); // here I can log the correct value of the observable ( {data: [{id:1}]})
    });
    expect(fixture.debugElement.query(By.css('custom-component'))).not.toBeNull(); // this fails
}));

Unfortunately everything I got is this

Error: Expected null not to be null.

There's no issue with the service or the observable itself, but it seems to me that for some reason the DOM doesn't trigger change detection with the async pipe which would render the component.

PS: When I am using getTestScheduler().flush() I get the error Can't subscribe to undefined instead.

Upvotes: 2

Views: 1165

Answers (2)

JB17
JB17

Reputation: 459

I solved it by creating another test suite, where I defined a value for the observable manually. I'm not entirely happy with this approach but it is a workaround for my problem.

Upvotes: 1

Robin Dijkhof
Robin Dijkhof

Reputation: 19288

Your subscribe is async. That means your console.log can fire after your last expect. At your last expect the data probably has not loaded yet so the value is undefined.

Using tick(100) instead of tick() will probably work. Note this will make your test 100ms slower.

You could also switch to async and wait for the observable to return a value:

it ('should display custom component', async(() => {
    ...
    // tick(); //dont call this
    ...
    await component.data$.pipe(take(1)).toPromise()
    expect(fixture.debugElement.query(By.css('custom-component'))).not.toBeNull(); 
}));

Upvotes: 0

Related Questions