Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock Angular 4.3 httpClient an error response in testing

I have a below interceptor auth-interceptor.service.ts

import {Injectable, Injector} from '@angular/core'; import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http'; import {Observable} from 'rxjs/Observable'; import {Cookie} from './cookie.service'; import {Router} from '@angular/router'; import {UserService} from './user.service'; import {ToasterService} from '../toaster/toaster.service'; import 'rxjs/add/operator/catch'; import 'rxjs/add/observable/throw';  @Injectable() export class AuthInterceptor implements HttpInterceptor {     constructor(private injector: Injector) {}      private handleError(err: HttpErrorResponse): Observable<any> {         let errorMsg;         if (err.error instanceof Error) {             // A client-side or network error occurred. Handle it accordingly.             errorMsg = `An error occurred: ${err.error.message}`;         } else {             // The backend returned an unsuccessful response code.             // The response body may contain clues as to what went wrong,             errorMsg = `Backend returned code ${err.status}, body was: ${err.error}`;         }         if (err.status === 401 || err.status === 403) {             this.injector.get(UserService).purgeAuth();             this.injector.get(ToasterService).showError(`Unauthorized`, errorMsg);             this.injector.get(Router).navigateByUrl(`/login`);         }         console.error(errorMsg);         return Observable.throw(errorMsg);     }      intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {         // Clone the request to add the new header.         const authReq = req.clone({headers: req.headers.set(Cookie.tokenKey, Cookie.getToken())});         // Pass on the cloned request instead of the original request.         return next.handle(authReq).catch(err => this.handleError(err));     } } 

Now I am trying to mock the http.get to throw the error, so that method handleError consoles the error message.

Below is my approach to the test case auth-interceptor.service.specs.ts

import {async, inject, TestBed} from '@angular/core/testing';  import {AuthInterceptor} from './auth-interceptor.service'; import {ApiService} from './api.service'; import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; import {environment} from '../../../environments/environment';  describe(`AuthInterceptor`, () => {     const somePath = `/somePath`;      beforeEach(() => {         TestBed.configureTestingModule({             imports: [HttpClientTestingModule],             providers: [AuthInterceptor, ApiService]         });     });      it(`should be created`, inject([AuthInterceptor], (service: AuthInterceptor) => {         expect(service).toBeTruthy();     }));       it(`should log an error to the console on error on get()`, async(inject([ApiService, HttpTestingController],         (apiService: ApiService, httpMock: HttpTestingController) => {             spyOn(console, 'error');             apiService.get(somePath).subscribe((res) => {                 console.log(`in success:`, res);             }, (error) => {                 console.log(`in error:`, error);             });              const req = httpMock.expectOne(`${environment.apiUri}${somePath}`);             req.flush({                 type: 'ERROR',                 status: 404,                 body: JSON.stringify({color: `blue`})             });             expect(console.error).toHaveBeenCalled();         }))     ); }); 

When flushing the response, I am not sure how to flush a error response, so that the method handleError will be called in my interceptor and that eventually calls console.error. Documentation doesn't have any example to my situation. Any help or suggestion is appreciated.

like image 342
Sai Ram Gupta Avatar asked Sep 03 '17 23:09

Sai Ram Gupta


People also ask

Which of the following can be used to mock the HTTPClient service of Angular?

While writing the Unit test for services using HTTPClient, you may find it is very useful to use HttpClientTestingModule. This module helps in testing and mocking especially data services that make HTTP calls to the servers.

Which method is used to mock the service method response in Angular?

Prefer spies as they are usually the best way to mock services. These standard testing techniques are great for unit testing services in isolation. However, you almost always inject services into application classes using Angular dependency injection and you should have tests that reflect that usage pattern.

What is HTTP error response in Angular?

A response that represents an error or failure, either from a non-successful HTTP status, an error while executing the request, or some other failure which occurred during the parsing of the response.

What is the correct way to trigger a click event on a button when testing an Angular component?

For click event we can use triggerEventHandler method of Angular DebugElement class. We can also call native JavaScript click method of button. On click of button, we call a component method and it is possible that our component method has other dependencies to execute.


1 Answers

The expectOne method in HttpTestingController class returns a TestRequest object. This TestRequest class has a flush method which can be used to deliver

both successful and unsuccessful responses.

We can resolve the request by returning a body along with some additional response headers (if any). Relevant info can be found here.

Now, coming back to the point how you can do this. You can customize the below code snippet as per your use case.

http = TestBed.get(HttpTestingController); let response: any; let errResponse: any; const mockErrorResponse = { status: 400, statusText: 'Bad Request' }; const data = 'Invalid request parameters'; apiService.get(somePath).subscribe(res => response = res, err => errResponse = err); http.expectOne('url/being/monitored').flush(data, mockErrorResponse); expect(errResponse).toBe(data); 

NOTE: At the time of writing this comment, statusText is required in mockErrorResponse. Related info can be found here.

P.S.: The error method of TestRequest class can be used to simulate network error in our test case, as it expects an instance of Error. The following code snippet shows that.

http.expectOne(someUrl).error(new ErrorEvent('network error')); 
like image 54
Rohit Sharma Avatar answered Sep 18 '22 14:09

Rohit Sharma