spy on method when unit testing http interceptor in angular

762 views Asked by At

I'm trying to unit test an http interceptor. The interceptor is used for authorization. I can test the basic success case, but I'm trying now to test the case where the API call fails because access token is out of date.

My question is, how do I spy on the AuthInterceptorService.getNewTokenAndRetryRequest() method? My interceptor & unit test code are below. When I run the unit tests, I get a "Error: Expected spy getNewTokenAndRetryRequest to have been called." error.

This is my unit test :

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        HttpClientTestingModule
      ],
      providers: [TokenStorageService,
  
      {
          provide: HTTP_INTERCEPTORS,
          useClass: AuthInterceptorService,
          multi: true
      }      
      
      ]

    });
    service = TestBed.inject(AuthInterceptorService);
  });


     it('should proceed to refresh token logic on 500 error', inject([HttpClient, HttpTestingController], (http: HttpClient, httpMock: HttpTestingController) => {
        let  thownError: Error = null;
    
        spyOn(service, "getNewTokenAndRetryRequest").and.stub();
    
    
        Object.defineProperty(window.document, 'cookie', {
          writable: true,
          value: 'access_token=mycookie',
        });
    
        http.get('/data').subscribe(
            response => {
                expect(response).toBeTruthy();
            }
        );
    
    
        const req = httpMock.expectOne(r =>
            r.headers.has('Authorization') && 
            r.headers.get('Authorization').startsWith('Bearer ewogIC') );
    
        req.flush("failure",{ status: 500, statusText: "500 error" });
    
        expect(service.getNewTokenAndRetryRequest).toHaveBeenCalled();
    
        httpMock.verify();
      }));

This is my code for the interceptor

 export class AuthInterceptorService implements HttpInterceptor {

  constructor(private tokenStorageService: TokenStorageService, private httpClient: HttpClient) { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<any> {
    const updatedHeaders:HttpHeaders = req.headers.set('Authorization', 'Bearer ' + this.tokenStorageService.getAccessToken());
    const updatedRequest = req.clone({headers: updatedHeaders})
    return next.handle(updatedRequest)
    .pipe(  
      catchError((error: HttpErrorResponse) => {
        if (error.status===500) {

          console.log('500 error, try to use the refresh token');
          try{
            this.getNewTokenAndRetryRequest(req);
            return of([]);
          }catch (error) {
            return throwError(error);
          }
        }else{
          let errorMsg =  `Error Code: ${error.status},  Message: ${error.message}`;

          return throwError(error.error);
        }
       
    })
   )
  }

  getNewTokenAndRetryRequest(req: HttpRequest<any>): void {
    //do stuff

  }
 
1

There are 1 answers

2
AliF50 On

There could be a race condition where we are asserting too early. Try to assert in the subscribe callback.

Try making the following changes (follow !!):

it('should proceed to refresh token logic on 500 error', inject([HttpClient, HttpTestingController], fakeAsync((http: HttpClient, httpMock: HttpTestingController) => {
        let  thownError: Error = null;
    
        // !! Assign this spy to a variable and remove .and.stub();
        const getNewTokenSpy = spyOn(service, "getNewTokenAndRetryRequest");
    
    
        Object.defineProperty(window.document, 'cookie', {
          writable: true,
          value: 'access_token=mycookie',
        });
    
        http.get('/data').subscribe(
            response => {
                expect(response).toBeTruthy();
                // !! move assertion here
                expect(service.getNewTokenAndRetryRequest).toHaveBeenCalled();
                // !! make sure this test below fails so we know this
                // subscribe callback is being traversed.
                // Delete this assertion once it fails.
                expect(1).toBe(2);
            }
        );
    
    
        const req = httpMock.expectOne(r =>
            r.headers.has('Authorization') && 
            r.headers.get('Authorization').startsWith('Bearer ewogIC') );
    
        req.flush("failure",{ status: 500, statusText: "500 error" });
    
        httpMock.verify();
      })));