BlackHoleGalaxy
BlackHoleGalaxy

Reputation: 9682

How to test (Jasmine) a service actually handles the error on mocked HTTP error

I have an Angular service, generated through Swagger.

The service just makes an HTTP call and return a list of Users. I managed to properly code tests case for "success" calls.

However, I struggle making the test for "fail" cases.

I tried to simply call the function and by Mocking a response, creating an "errored" response, but it doesn't seem to be able to detect an Exception was actually thrown.

Below you will find both the Service and my test case:

service.ts

getListUsers(): Observable<UsersListModel | null> {
    let url_ = this.baseUrl + "/api/users";
    url_ = url_.replace(/[?&]$/, "");

    const content_ = "";

    let options_ = {
        body: content_,
        method: "get",
        headers: new Headers({
            "Content-Type": "application/json; charset=UTF-8", 
            "Accept": "application/json; charset=UTF-8"
        })
    };

    return this.http.request(url_, options_).flatMap((response_) => {
        return this.processGetListUsers(response_);
    }).catch((response_: any) => {
        if (response_ instanceof Response) {
            try {
                return this.processGetListActivities(response_);
            } catch (e) {
                return <Observable<UsersListModel>><any>Observable.throw(e);
            }
        } else
            return <Observable<UsersListModel>><any>Observable.throw(response_);
    });
}

protected processGetListUsers(response: Response): Observable<UsersListModel | null> {
    const status = response.status; 

    if (status === 200) {
        const responseText = response.text();
        let result200: UsersListModel | null = null;
        let resultData200 = responseText === "" ? null : JSON.parse(responseText, this.jsonParseReviver);
        result200 = resultData200 ? UsersListModel.fromJS(resultData200) : <any>null;
        return Observable.of(result200);
    } else if (status !== 200 && status !== 204) {
        const responseText = response.text();
        return throwException("An unexpected server error occurred.", status, responseText);
    }
    return Observable.of<UsersListModel | null>(<any>null);
}

spec file

describe('ERROR on Get Users List', () => {
    let backend: MockBackend;
    let service: UserService;
    let response: Response;

    beforeEach(async(() => {    
      TestBed.configureTestingModule({
        imports: [HttpModule],
        providers: [
          MockBackend,
          { provide: XHRBackend, useClass: MockBackend },

          UserService
        ]
      });
    }));

    beforeEach(inject([Http, XHRBackend], (http: Http, be: MockBackend) => {
      backend = be;
      service = new UserService(http);
      let options = new ResponseOptions({status: 500, body: 'API Error'});
      response = new Response(options);
    }));

    it('should handle an API error on getListUsers()', async(inject([], () => {
        backend.connections.subscribe((c: MockConnection) => {
          c.mockRespond(response);
        });

        expect(() => service.getListUsers()).toThrowError();
      }))
    );

  });

With this test case, the test fails without more information:

Expected function to throw an Error.

I even tried to use the subscription to detect something:

it('should handle an API error on getListUsers()', async(inject([], () => {
    backend.connections.subscribe((c: MockConnection) => {
      c.mockRespond(response);
    });

    spyOn(console, 'error');

    service.getListUsers()
      .subscribe(
      res => {},
      err => { 
        console.error(err)
      });

    expect(console.error).toHaveBeenCalled();
  }))
);

Here I can see the error thrown in console, but the console.error is never triggered.

So, maybe my test case is not appropriate.

Upvotes: 2

Views: 2551

Answers (1)

Aluan Haddad
Aluan Haddad

Reputation: 31873

That assertion helper isn't going to work because the error gets wrapped and propagated through the Observable's error notification channel.

For this to work you need to subscribe to the observable and assert accordingly

service.getListActivities().subscribe(
  () => test.failure('expected an error but observable terminated successfully.'),

  () => test.success('observable threw an error as expected.')
);

The example is generalized as the specific helpers that you call will depend on the test framework in use.

Upvotes: 1

Related Questions