I have a somewhat complex situation where, do to backend implementation, I need to poll the backend until either
The code works, and testing against the backend succeeded in all three situations. I have also succeeded in testing the first and second situations, if the result is instantaneous (mocking the response of complete / fail immediately, without getting onGoing
in the middle). However, I have not been able to test the third case. I got different errors with each attempt, so now I am turning to the collective mind of S.O. for some help :-)
ts file:
doAction(content): Observable<resPassType> {
return this.postAction(content) // private function that just calls HTTP Post at url1, and returns an actionId for polling
.pipe(concatMap(action => this.poll(action))); // see below
}
private poll(action, retryCount = config.retryCount): Observable<resPassType>{
return time(config.retryTime).pipe(switchMap(() =>
this.getActionStatus(action) // private function that calls HTTP get at url2/action.id
.pipe(concatMap ( res=> {
retryCount--;
if(retryCount < 0)
return throwError('Action Timed Out'); // this is essentially what I want to test
switch(res.action.state.toLowerCase()) {
case 'completed':
return of(resPassTypeData);//taken from within res.action. Successfully tested
case 'failed':
return throwError('Action Failed'); // successfully tested
case 'ongoing':
return this.poll(res.action, retryCount)
default:
return throwError('unexpeceted action state');
}
}))));
}
Seeing the successful tests may help you help me, so included here is the test for failure
it('should throw error if action failed', fakeAsync(()=>{
const mockGetActionResponse = {'action' : {'id' : mockActionId, state: 'Failed' }};
service.doAction(mockContent).subscribe(
() => {},
err => expect(err).toEqual('Action Failed');
);
const postCall = httpTestingController.expectOne(url1);
expect(postCall.request.method).toEqual('POST');
expect(postCall.request.body).toEqual(mockContent);
postCall.flush(mockPostActionResponse);
tick(config.retryTime);
const getCall = httpTestingController.expectOne(url2/mockActionId);
expect(getCall .request.method).toEqual('Get');
getCall.flush(mockGetActionResponse );
}));
This test works. How can I write the test for case 3? Writing the same test and changing the state to onGoing
and changing the error expectation left me with the error
error expected no open requests found 1
My hunch is that is has something to do with the recursion or the delay or something, since this is the only test case where poll
gets called more than once. Any leads would be greatly appreciated :-)
EDIT Code has been updated thanks to comments and improvements from @Andrei. Issue still persists.
Answer found by @Andrei in the comments.
Working code now looks like this:
it('should throw error if action timed out',()=>{
const mockGetActionResponse = {'action' : {'id' : mockActionId, state: 'onGoing' }};
service.doAction(mockContent).subscribe(
() => {},
err => expect(err).toEqual('Action Timed Out');
);
const postCall = httpTestingController.expectOne(url1);
expect(postCall.request.method).toEqual('POST');
expect(postCall.request.body).toEqual(mockContent);
postCall.flush(mockPostActionResponse);
for( let i=0; i<config.retryCount; i++) {
tick(config.delayTime);
const getCall = httpTestingController.expectOne(url2);
expect(getCall.request.method).toEqual('GET');
getCall.flush(mockGetActionResponse)
}
})