Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing an HTTP request with retry() and HttpClientTestingModule

I want to test an HTTP call error response with the HttpClientTestingModule. This works fine until I add a rxjs retry(2) to the HTTP call. Then, the test obviously complains that an unexpected request is found:

Expected no open requests, found 1

But now, I don't know how to expect two requests using the HttpTestingController:

service.ts

@Injectable()
export class Service {
  constructor(private http: HttpClient) { }

  get() {
    return this.http.get<any>('URL')
      .pipe(
        retry(2),
        catchError(error => of(null))
      )
  }
}

service.spec.ts

describe('Service', () => {
  let httpTestingController: HttpTestingController;
  let service: Service;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [Service],
      imports: [HttpClientTestingModule]
    });

    httpTestingController = TestBed.get(HttpTestingController);
    service = TestBed.get(Service);
  });

  afterEach(() => {
    httpTestingController.verify();
  });

  it('should handle 404 with retry', () => {
    service.get().subscribe((data: any) => {
      expect(data).toBe(null);
    });
    expectErrorResponse(404);
  });

  function expectErrorResponse(status: number) {
    const requests = httpTestingController.match('URL');
    // fails -> finds only one request
    expect(requests.length).toBe(2);
    requests.forEach(req => req.flush('error', {status, statusText: 'Not Found'}));
  }
});

If I remove the expect(requests.length).toBe(2), the test will fail with the error message from before.

Running Example

You can try it out with this Stackblitz

like image 683
Kim Kern Avatar asked Mar 22 '18 21:03

Kim Kern


People also ask

How would you write code to modify the response from an HTTP GET?

catch( (error: Response) => { return Observable. throw(error); } );


1 Answers

The fundamentals of Angular - HttpClient - retry() states:

The RxJS library offers several retry operators that are worth exploring. The simplest is called retry() and it automatically re-subscribes to a failed Observable a specified number of times. Re-subscribing to the result of an HttpClient method call has the effect of reissuing the HTTP request.

So every time you call flush it leaves behind an open request. You just need to repeat the request handling and flushing as many times as the service retries the request.

it('can test for 404 error', () => {
  const emsg = 'deliberate 404 error';

  testService.getData().subscribe(
    data => fail('should have failed with the 404 error'),
    (error: HttpErrorResponse) => {
      expect(error.status).toEqual(404, 'status');
      expect(error.error).toEqual(emsg, 'message');
    }
  );

  const retryCount = 3;
  for (var i = 0, c = retryCount + 1; i < c; i++) {
    let req = httpTestingController.expectOne(testUrl);
    req.flush(emsg, { status: 404, statusText: 'Not Found' });
  }
});
like image 139
gaborb Avatar answered Sep 21 '22 19:09

gaborb