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.