Reputation: 1211
I have the below http interceptor in my angular application and I would like to unit test the same using Jasmine. I googled some of them and tried but its not working as expected. Please find the below HttpInterceptorService.ts file code
export class HttpInterceptorService Implements HttpInterceptor {
counter = 0;
constructor(private loaderService: LoaderService) { }
intercept(req: HttpRequest<any>, next: HttpHandler) {
if (req.url !== '/getUsers') {
this.counter ++;
}
this.loaderService.setStatus(true);
return next.handle(req).pipe(
finalize(() => {
if (req.url !== 'getUsers') {
this.counter --;
}
if (this.counter === 0) {
this.loaderService.setStatus(false);
}
};
);
}
}
Below are the HttpInterceptor.service.spec.ts file code which I tried as of now. Im not sure how to test the particular method in it.
describe('HttpInterceptorService', () => {
let httpService: HttpService;
let httpMock: HttpTestingController;
let interceptor: HttpInterceptorService;
beforeEach(()=> {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
HttpService,
{provide:HTTP_INTERCEPTOR, useClass: HttpInterceptorService, multi: true},
]
});
httpService = TestBed.get(HttpService);
httpMock = TestBed.get(HttpTestingController);
interceptor = TestBed.get(HttpInterceptorService);
});
it('should increment the counter for all api's expect getUsers', ()=> {
httpService.get('getAdminList').subscribe(res => {
expect(res).toBeTruthy();
expect(interceptor.counter).toBeGreaterThan(0);
});
});
});
after checking the reference code I'm able to cover few lines of code with above changes. But I'm still not able to cover the finalize method. Request to kindly help.
Upvotes: 8
Views: 27670
Reputation: 18809
Remove HttpInterceptorService
from the providers because you are already providing it on the next line with { provide:HTTP_INTERCEPTOR, ...
. Try to follow this guide: https://alligator.io/angular/testing-http-interceptors/. It seems like you need to have a service that actually makes API calls. Try to follow this guide as well: https://www.mobiquity.com/insights/testing-angular-http-communication
I think to make an HTTP call, you can just do httpClient.get('www.google.com').subscribe()
and you shouldn't need an actual service (DataService
) like the first guide shows.
Edit:
describe('HttpInterceptorService', () => {
let httpService: HttpService;
let httpMock: HttpTestingController;
let interceptor: HttpInterceptorService;
// mock your loaderService to ensure no issues
let mockLoaderService = { setStatus: () => void };
beforeEach(()=> {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
HttpService,
{provide:HTTP_INTERCEPTOR, useClass: HttpInterceptorService, multi: true},
// provide the mock when the unit test requires
// LoaderService
{ provide: LoaderService, useValue: mockLoaderService },
]
});
httpService = TestBed.get(HttpService);
httpMock = TestBed.get(HttpTestingController);
interceptor = TestBed.get(HttpInterceptorService);
});
it("should increment the counter for all api's except getUsers", ()=> {
httpService.get('getAdminList').subscribe(res => {
expect(res).toBeTruthy();
expect(interceptor.counter).toBeGreaterThan(0);
});
});
// add this unit test
it('should decrement the counter for getUsers', ()=> {
httpService.get('getUsers').subscribe(res => {
expect(res).toBeTruthy();
expect(interceptor.counter).toBe(0);
});
});
});
Upvotes: 3
Reputation: 116
Don’t know if the topic is still hot but: https://medium.com/@js_9757/angular-unit-test-the-http-interceptor-c2464cf8e8da
Example with Angular 18 and Jest
Given that Interceptor code:
import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class AccessTokenInterceptor implements HttpInterceptor {
private readonly TOKEN_KEY: string = 'access_token';
public intercept(httpRequest: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(httpRequest.clone(
{
setHeaders: this.getAuthInterceptorToken(),
},
))
}
private getAuthInterceptorToken(): any {
return {
'Authorization': `Bearer ${localStorage.getItem(this.TOKEN_KEY)}`,
};
}
}
Can test with this test code:
import { TestBed } from '@angular/core/testing';
import { HTTP_INTERCEPTORS, HttpClient, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { AccessTokenInterceptor } from './http.interceptor';
describe('HttpInterceptor', () => {
let httpMock: HttpTestingController;
let httpClient: HttpClient;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
{ provide: HTTP_INTERCEPTORS, useClass: AccessTokenInterceptor, multi: true },
],
});
httpMock = TestBed.inject(HttpTestingController);
httpClient = TestBed.inject(HttpClient);
})
afterEach(() => {
httpMock.verify();
});
it('should add Authorization header', () => {
jest.spyOn(Storage.prototype, 'getItem');
Storage.prototype.getItem = jest.fn().mockReturnValue('test_token')
httpClient.get('/test').subscribe();
const req = httpMock.expectOne('/test');
expect(req.request.headers.has('Authorization')).toBeTruthy();
expect(req.request.headers.get('Authorization')).toBe('Bearer test_token');
});
})
May this is helpful.
Upvotes: 2
Reputation: 1211
the below code helps to cover the code inside finalize operator.
const next: any = {
handle: () => {
return Observable.create(subscriber => {
subscriber.complete();
});
}
};
const requestMock = new HttpRequest('GET', '/test');
interceptor.intercept(requestMock, next).subscribe(() => {
expect(interceptor.counter).toBeGreaterThan(0);
});
Upvotes: 9
Reputation: 1378
Living Example@
describe('AuthHttpInterceptor', () => {
let http: HttpClient,
httpTestingController: HttpTestingController,
mockAuthService: AuthorizationService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, SharedModule],
providers: [
{
provide: AuthorizationService,
useClass: MockAuthorizationService
},
{
provide: HTTP_INTERCEPTORS,
useClass: AuthorizationInterceptor,
multi: true
},
// One of these tests trigger a console.error call and is expected
// Mocking the logger prevents this otherwise another test run outside this suite
// to prevent console.error calls will fail.
{
provide: LoggerInjectionToken,
useValue: mockLogger
}]
});
http = TestBed.inject(HttpClient);
httpTestingController = TestBed.inject(HttpTestingController);
mockAuthService = TestBed.inject(AuthorizationService);
});
Example Test:
it('will refresh token and re-issue request should 401 be returned.', (() => {
spyOn(mockAuthService, 'requestNewToken').and.callFake(() => {
return of({
renewed: true,
accessToken: 'token'
});
});
http.get('/data')
.subscribe((data) => {
expect(data).toEqual('Payload');
});
const failedRequest = httpTestingController.match('/data')[0];
failedRequest.error(new ErrorEvent('Er'), { status: 401 });
const successReq = httpTestingController.match('/data')[0];
successReq.flush('Payload', { status: 200, statusText: 'OK' });
expect(mockAuthService.requestNewToken).toHaveBeenCalled();
httpTestingController.verify();
}));
Depends on what's needed directly
Upvotes: 1