Here is my working solution, similar to the above suggestions, but with more clarity:
it('should log an error to the console on error', async(inject([AjaxService, MockBackend], ( ajaxService: AjaxService, mockBackend: MockBackend) => { service = ajaxService; backend = mockBackend; backend.connections.subscribe((connection: MockConnection) => { const options: any = new ResponseOptions({ body: { error: 'Some strange error' }, status: 404 }); const response: any = new Response(options); connection.mockError(response); }); spyOn(console, 'error'); service.get('/bad').subscribe(res => { console.log(res); // Object{error: 'Some strange error'} }, e => { expect(console.error).toHaveBeenCalledWith('404 - Some strange error'); }); })));
Full working help code:
Below are all possible test scenarios. Note. Do not worry about AjaxService . This is my regular shell on the angular http service, which is used as an interceptor.
ajax.service.spec.ts
import { AjaxService } from 'app/shared/ajax.service'; import { TestBed, inject, async } from '@angular/core/testing'; import { Http, BaseRequestOptions, ResponseOptions, Response } from '@angular/http'; import { MockBackend, MockConnection } from '@angular/http/testing'; describe('AjaxService', () => { let service: AjaxService = null; let backend: MockBackend = null; beforeEach(async(() => { TestBed.configureTestingModule({ providers: [ MockBackend, BaseRequestOptions, { provide: Http, useFactory: (backendInstance: MockBackend, defaultOptions: BaseRequestOptions) => { return new Http(backendInstance, defaultOptions); }, deps: [MockBackend, BaseRequestOptions] }, AjaxService ] }); })); it('should return mocked post data', async(inject([AjaxService, MockBackend], ( ajaxService: AjaxService, mockBackend: MockBackend) => { service = ajaxService; backend = mockBackend; backend.connections.subscribe((connection: MockConnection) => { const options = new ResponseOptions({ body: JSON.stringify({ data: 1 }), }); connection.mockRespond(new Response(options)); }); const reqOptions = new BaseRequestOptions(); reqOptions.headers.append('Content-Type', 'application/json'); service.post('', '', reqOptions) .subscribe(r => { const out: any = r; expect(out).toBe(1); }); }))); it('should log an error to the console on error', async(inject([AjaxService, MockBackend], ( ajaxService: AjaxService, mockBackend: MockBackend) => { service = ajaxService; backend = mockBackend; backend.connections.subscribe((connection: MockConnection) => { const options: any = new ResponseOptions({ body: { error: 'Some strange error' }, status: 404 }); const response: any = new Response(options); connection.mockError(response); }); spyOn(console, 'error'); service.get('/bad').subscribe(res => { console.log(res); // Object{error: 'Some strange error'} }, e => { expect(console.error).toHaveBeenCalledWith('404 - Some strange error'); }); }))); it('should extract mocked data with null response', async(inject([AjaxService, MockBackend], ( ajaxService: AjaxService, mockBackend: MockBackend) => { service = ajaxService; backend = mockBackend; backend.connections.subscribe((connection: MockConnection) => { const options = new ResponseOptions({ }); connection.mockRespond(new Response(options)); }); const reqOptions = new BaseRequestOptions(); reqOptions.headers.append('Content-Type', 'application/json'); service.get('test', reqOptions) .subscribe(r => { const out: any = r; expect(out).toBeNull('extractData method failed'); }); }))); it('should log an error to the console with empty response', async(inject([AjaxService, MockBackend], ( ajaxService: AjaxService, mockBackend: MockBackend) => { service = ajaxService; backend = mockBackend; backend.connections.subscribe((connection: MockConnection) => { const options: any = new ResponseOptions({ body: {}, status: 404 }); const response: any = new Response(options); connection.mockError(response); }); spyOn(console, 'error'); service.get('/bad').subscribe(res => { console.log(res); // Object{error: 'Some strange error'} }, e => { expect(console.error).toHaveBeenCalledWith('404 - {}'); }); // handle null response in error backend.connections.subscribe((connection: MockConnection) => { connection.mockError(); }); const res: any = null; service.get('/bad').subscribe(res, e => { console.log(res); }, () => { expect(console.error).toHaveBeenCalledWith(null, 'handleError method with null error response got failed'); }); }))); });
ajax.service.ts
import { Injectable } from '@angular/core'; import { Http, Response, RequestOptionsArgs, BaseRequestOptions } from '@angular/http'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/catch'; import 'rxjs/add/operator/map'; import 'rxjs/add/observable/throw'; @Injectable() export class AjaxService { constructor( private http: Http, ) { } get(url: string, options?: RequestOptionsArgs): Observable<Response> { options = this.getBaseRequestOptions(options); options = this.setHeaders(options); return this.http.get(url, options) .map(this.extractData) .catch(this.handleError); } post(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> { options = this.getBaseRequestOptions(options); options = this.setHeaders(options); return this.http.post(url, body, options) .map(this.extractData) .catch(this.handleError); } private extractData(res: Response) { const body = res.json(); const out = body && body.hasOwnProperty('data') ? body.data : body; return out; } private handleError(error: Response | any) { let errMsg: string; if (error instanceof Response) { const body = error.json() || ''; const err = body.error || JSON.stringify(body); errMsg = `${error.status} - ${error.statusText || ''}${err}`; } else { errMsg = error.message ? error.message : error.toString(); } console.error(errMsg); return Observable.throw(errMsg); } private getBaseRequestOptions(options: RequestOptionsArgs = new BaseRequestOptions()) { return options; } private setHeaders(options: RequestOptionsArgs) { if (!options.headers || !options.headers.has('Content-Type')) { options.headers.append('Content-Type', 'application/json'); } return options; } }