Reputation: 687
I'm working on a toy project to learn unit testing in Angular (replaced Karma with Jest). I've seen similar questions but all of them handle subscriptions differently, so I wasn't able to get the exact answer to work for me.
TL;DR: I'm trying to test a BehaviorSubject, but rather than the value I'm expecting, it's sending it's initial value to me. I understand the ordering of the subscription in the test matters, but I couldn't figure the proper ordering yet.
I have a BehaviorSubject in a service:
export class SomeService {
// ...
remainingSeconds: number;
private _seconds: Subject<number> = new BehaviorSubject(RegularTimerSeconds.WORK_TIME);
public readonly seconds$: Observable<number> = this._seconds.asObservable();
setTimer(seconds: number) {
this.remainingSeconds = seconds;
this._seconds.next(this.remainingSeconds);
}
// ...
}
and a listener on a component, which subscribes to the Observable using the async pipe in the template
export class SomeComponent implements OnInit, OnDestroy {
// ...
remainingSeconds$ = this.timer.seconds$.pipe(tap(seconds => console.log(`remainingSeconds$: ${seconds}`)));
constructor(public timer: TimerService) {}
}
Subscription happens in the template as follows
<ng-container *ngIf="{
remainingSeconds: remainingSeconds$ | async,
currentState: currentState$ | async,
informationText: informationText$ | async,
doneCounter: doneCounter$ | async
} as data">
<!-- ... -->
<div class="regular-timer-container" *ngIf="data.remainingSeconds !== undefined">
<p>Remaining seconds: {{ data.remainingSeconds }}</p>
</div>
</ng-container>
I would like to test that when the setTimer
function in the service is called, the remainingSeconds$ observable in the component also gets the correct value. Especially, if the "remainingSeconds" value in the service is undefined, I'd like to make sure that the remainingSeconds$ observable gets the updated undefined value, and thus the appropriate UI element is hidden.
The problem I'm having with the test (for which the code is below) is that when I call service.setTimer(undefined)
from the component, I'm getting the initial value of the BehaviorSubject rather than the undefined
value I wanted (which makes the test useless).
it(`shouldn't render the timer component if the seconds value is unset`, async(() => {
component.remainingSeconds$
.subscribe(
seconds => {
expect(seconds).toEqual(undefined);
fixture.detectChanges();
const timerContainerDom: DebugElement = fixture.debugElement;
const regularTimerComponent = timerContainerDom.query(By.directive(AppComponent));
expect(regularTimerComponent).toBeFalsy();
}
);
timerService.setTimer(undefined);
}));
I'm adding a stackblitz link, but sadly I couldn't run npm test on there. Hopefully it'll give some basic idea as to what I'm trying to accomplish.
Upvotes: 0
Views: 8311
Reputation: 11979
You could quickly solve this with:
component.remainingSeconds$.pipe(skip(1)).subscribe(...)
Upvotes: 4
Reputation: 10979
Try
TestBed.configureTestingModule({
declarations: [
AppComponent
],
providers: [
TimerService
]
}).compileComponents();
and remove TestBed.inject
Upvotes: 0