I have a method in a service that sends post
calls recursively, and uses map
from rxjs to chain them.
bulkPostRecursive(offset: number, limit: number, ids: string[]): Observable<void> {
const filteredIds: string[] = ids.slice(offset, offset + limit);
const end: number = ids.length;
const data: Record<string, string[]> = {
ids: filteredIds,
};
if (offset + limit < end) {
return this.bulkPostRecursive(offset + limit, limit, ids).pipe(
map((): void => {
this.apiCallService.post('/api_url', data); // this part works as expected
}),
);
} else {
return this.apiCallService.post('/api_url', data); // this part works as expected
}
}
bulkPost(ids: string[]): Observable<void> {
const offset: number = 0;
const limit: number = 2; // value used just for testing purposes
return this.bulkPostRecursive(offset, limit, ids);
}
To test the section with this.bulkPostRecursive(offset + limit, limit, idList).pipe
, the following test is used:
it('bulkPostRecursive(): should call bulkPostRecursive', () => {
const bulkPostRecursiveSpy = jest.spyOn(service, 'bulkPostRecursive');
apiCallService.post.mockReturnValue(of({'somevalue': 'somevalue'}));
service.bulkPostRecursive(0, 4, ['id','id','id,'id']);
expect(bulkPostRecursiveSpy).toHaveBeenCalled();
expect(apiCallService.post).toHaveBeenCalled(); // here is where the error occurs
});
The following error occurs:
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
apiCallService.post is not getting called, please help on how to reach this branch properly.
In you example you are not subscribing to your observable.
Observable are considered "cold" that means they need a subscriber to execute. In angular the best way to subscribe to an obersable is to use async pipe
<div>observable$ | async<div>
For post calls its common the need to subscribe in your code.
observable$.subscribe()
Just be careful with memory leaks doing that way, to prevent that, I normally use one of the options:
// 1 store the subscription in a value and manually unsubscribe
subs = observable$.subscribe();
ngOnDestroy() {
subs?.unsubscribe();
}
// 2 make sure to call only once
observable$.pipe(take(1)).subscribe();
// 3 create an Observable to complete other observables
// good approach for completing multiple observables
complete = new Subject();
observable$.pipe(takeUntil(this.complete)).subscribe();
observable2$.pipe(takeUntil(this.complete)).subscribe();
ngOnDestroy() {
complete.next();
}