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!
Defining
userService.isUserLoggedIn$ = cold('-a|', {a: true});
anduserService.user$ = cold('-a|', {a: {name: 'foo'});
afterTestBed.configureTestingModule
is too late. These values have already been injected andisUserLoggedIn$
anduser$
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 theisUserLoggedIn$
anduser$
before theTestBed.configureTestingModule
.