user9063326
user9063326

Reputation:

Jasmine: Unit test http service error observable

I am pretty confused on how to mock that my http service catches an observable error. I have read through the angular documentation but I do not understand how I would structure the test. I would like to mock an error in my service, then check that it catches an error observable. I have another service that creates and throws the error observable. The rest of my services just catch it when encountering an error. I am honestly very confused on how to implement this. Here is a simple verison of my service, and also the error service:

getData(url: string): Observable<any> {
    return this.http.get(url)
       .map(this.extractData)
        .catch(this.handleErrorObservable);
}

  handleErrorObservable(error: Response | any) {
    console.error(error.message || error);
    return Observable.throw(error.message || error);
}

Now how would I simulate an error? Do I need to spy on the service and make it fail? Would something along the lines of spyOn(service, 'getData).and.returnValue(Observable.throw({status: 404}))) or am I looking at this the wrong way? Thanks.

EDIT: Code with Castro Roy's solution.

import {HttpErrorResponse, } from '@angular/common/http';
import { TestBed, inject } from '@angular/core/testing'
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'
import {HttpService} from '../services/http.service'

fdescribe('MyService', () => {
    let httpTestingController: HttpTestingController;

    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [HttpClientTestingModule],
            providers: [HttpService]
        });
        httpTestingController = TestBed.get(HttpTestingController);
    });

    afterEach(() => {

        httpTestingController.verify();
    });

    it('should be created', inject([HttpService], (service: HttpService)=> {
        expect(service).toBeTruthy();
    }));

    it('should handleErrorObservable', inject([HttpService], (service: HttpService) => {
        const urlString = '/data';
        const emsg = 'deliberate 404 error';

        spyOn(service, 'handleErrorObservable').and.callThrough();

        service.getData(urlString).subscribe(
            data => fail('should have failed with the 404 error'),
            (error: HttpErrorResponse) => {

              expect(service.handleErrorObservable).toHaveBeenCalled(); // check if executed

              expect(error.status).toEqual(404, 'status');
              expect(error.error).toEqual(emsg, 'message');

             });

        const req = httpTestingController.expectOne(urlString);


        req.flush(emsg, { status: 404, statusText: 'Not Found' });
      }));
});

Upvotes: 7

Views: 28971

Answers (1)

Castro Roy
Castro Roy

Reputation: 7823

spyOn(service, 'getData').and.returnValue(Observable.throw({status: 404}))) migth be useful if you are trying to mock getData to some other service that use it, but handleErrorObservable will never be executed. So, if you want to test that handleErrorObservable have been called, something like this should help you:

import { HttpErrorResponse } from '@angular/common/http';
import { TestBed, inject } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';

import { MyService } from './MyService';

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

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

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

  afterEach(() => {
    // After every test, assert that there are no more pending requests.
    httpTestingController.verify();
  });

  it('should be created', inject([MyService], (service: MyService) => {
    expect(service).toBeTruthy();
  }));

  it('should handleErrorObservable', inject([MyService], (service: MyService) => {
    const urlString = '/data';
    const emsg = 'deliberate 404 error';

    spyOn(service, 'handleErrorObservable').and.callThrough();

    service.getData(urlString).subscribe(
        data => fail('should have failed with the 404 error'),
        (error: HttpErrorResponse) => {

          expect(service.handleErrorObservable).toHaveBeenCalled(); // check if executed

          expect(error.status).toEqual(404, 'status');
          expect(error.error).toEqual(emsg, 'message');
        }
    });

    const req = httpTestingController.expectOne(urlString);

    // Respond with mock error
    req.flush(emsg, { status: 404, statusText: 'Not Found' });
  }));
});

Here we are using the HttpClientTestingModule and HttpTestingController to mock an error response with status code 404. Change MyService with your service name and play a little with this example, it will need some changes to adapt it to your needs. More info in the docs

Also, take a look to another question where are using a different way of mocking the HttpClient service.

Hope it helps.

Upvotes: 13

Related Questions