Check out myAngular article series with live demos and Facebook group Angular - Advanced Topics

Unit Testing RxJs Retries

Torgeir "Tor" Helgevold
- JavaScript Developer and Blogger
Published: Mon Sep 11 2017

In a previous article I showed how to add conditional retry logic to RxJs based http requests. In the following post I will show how to unit test the retry logic.

I recommend reading my other article first, but you can also just look at the code under test below:

Code Under Test

@Injectable() export class DataService { delay = 1000; retries = 5; constructor(private http: HttpClient) {} getDataWithConditionalRetry(url) { return this.http .get(url) .retryWhen(error => { return error .flatMap((error: any) => { if(error.status === 503) { return Observable.of(error.status).delay(this.delay) } return Observable.throw({error: 'No retry'}); }) .take(this.retries) .concat(Observable.throw({error: `Sorry, there was an error after ${this.retries} retries`})); }); } }

The code is essentially identical to the code in the original article. The only tweak I made was making the delay and retry count configurable. I did this since it’s undesirable to wait up to six seconds for retries in a unit test. Instead I set the delay to a very short interval in my test.

I have created two tests to test that the retry logic triggers (or doesn’t trigger) correctly.

Tests

import { TestBed } from '@angular/core/testing'; import { HttpClient } from '@angular/common/http'; import { DataService } from './app.component'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { Observable } from 'rxjs/Observable'; describe('DataService', () => { let service: DataService; let http: HttpClient; beforeEach(() => { TestBed.configureTestingModule({ imports: [ HttpClientTestingModule ], providers: [ DataService ] }); service = TestBed.get(DataService); service.delay = 1; service.retries = 5; http = TestBed.get(HttpClient); }); it('should retry 5 times if status is 503', (done) => { spyOn(http, 'get').and.returnValue(Observable.throw({status: 503})); service.getDataWithConditionalRetry('/api/data') .subscribe(res => {}, (error) => { expect(error).toEqual({error: 'Sorry, there was an error after 5 retries'}); done(); }); }); it('should not retry if status is not 503', (done) => { spyOn(http, 'get').and.returnValue(Observable.throw({status: 404})); service.getDataWithConditionalRetry('/api/data') .subscribe(res => {}, (error) => { expect(error).toEqual({error: 'No retry'}); done(); }); }); });

The tests are pretty straightforward. The key is really the mocking of the http.get call. As you can see, I am spying on http.get, and setting a status that will trigger a certain retry behavior.

Then in the subscriber’s error handler I can assert that the correct error message was returned.

I am using the “done” flag here to make sure the test doesn’t complete until all retries have completed.

If you liked this article, share it with your friends on social media:

We also have a new Facebook group about advanced Angular topics.

I invite you to follow me on twitter