Angular Unit Test - nested async pipes

386 views Asked by At

Consider the following code:

app.component.ts

@Component({
  selector: 'app-component',
  templateUrl: './app-component.html',
  styleUrls: ['./app-component.scss']
})
export class AppComponent implements OnInit {

  constructor(
    private userService: UserService
  ) { }

  ngOnInit() {}
}


app.component.html

<div *ngIf="userService.isUserLoggedIn$ | async">
 <span class="username">{{(userService.user$ | async)?.name}}</span>
</div>

This is a simplification for one of my components. I tried to write a basic unit test:

  it('should display the username', () => {
    userService.isUserLoggedIn$ = cold('-a|', {a: true});
    userService.user$ = cold('-a|', {a: {name: 'foo'});

    fixture.detectChanges();
    getTestScheduler().flush();
    fixture.detectChanges();


    const el = fixture.debugElement.query(By.css('.username')).nativeElement as HTMLSpanElement;
    expect(el.innerText.trim()).toBe('foo);
  });

I'm using the jasmine marbles library.

The problem is that the span doesn't have the correct value, it's always empty. I guess is because of the 2 nested async pipes. After the observables are flushed, the ngIf condition becomes true, the span is created and a new subscription for the user$ is created. But because the observable completed, the span will be empty.

I'm not sure if this is the correct explanation...

My question is how should I test this kind of scenario?

Thanks!

1

There are 1 answers

0
AliF50 On

Defining userService.isUserLoggedIn$ = cold('-a|', {a: true}); and userService.user$ = cold('-a|', {a: {name: 'foo'}); after TestBed.configureTestingModule is too late. These values have already been injected and isUserLoggedIn$ and user$ are values that are "static". Setting them at a later point in time doesn't tell the observable sequence to update this (no emissions happen).

When you mock userService, attach the isUserLoggedIn$ and user$ before the TestBed.configureTestingModule.