I recently needed to write some tests for an Angular Interceptor, and could not find easy / good guidance out there, so thought it'd make a good blog post.

What is an Interceptor

An interceptor is a mechanism that is run for every HTTPClient call made by your service. You can use this to add processing to HTTPClient calls. Some uses cases are to add specialized headers, such as authentication headers, to the call. We've also used this to show a generic error upon response from the service.

I'm going to unit test the second case.

The Interceptor Code

You can create a base interceptor with the Angular CLI:

view plain print about
1ng generate interceptor http-403

You'll see something like this:

view plain print about
1@Injectable()
2export class Http403Interceptor implements HttpInterceptor {
3
4 constructor() {}
5
6 intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
7 return next.handle(request);
8 }
9}

To handle a generic error we'll want to pipe off the return request and check for a 403 status inside a catchError operator:

view plain print about
1return next.handle(request).pipe(
2 catchError((err: HttpErrorResponse) =>
{
3 if (err.status === 403) {
4 // do something
5 this.errorOccurred = true;
6 }
7 return throwError(() => err);
8 })
9 );

The interceptor had to do something for us to test, so I toggled an instance variable on the Interceptor class, be sure to define that variable:

view plain print about
1errorOccurred = false;

In a real world scenario, we'd probably do something more complex such as show a warning to the user.

The Tests

The Angular CLI gave us a generic test, but we'll need to expand that. First, add some variables:

view plain print about
1let httpClient: HttpClient;
2 let httpMock: HttpTestingController;
3 let interceptor: Http403Interceptor;

We have an instance of the Interceptor. And also an instance of an HttpClient to and HttpTestingController which will be used to make mock HTTP Calls which will be intercepted. Let's add in a URL to load and an error event:

view plain print about
1const route = '/fakeUrl';
2 const error: ProgressEvent = new ProgressEvent('error');

Next we need to define the TestBed, which I do in a beforeEach() block:

view plain print about
1beforeEach(() => TestBed.configureTestingModule({
2 imports: [HttpClientTestingModule],
3 providers: [
4 {
5 provide: HTTP_INTERCEPTORS,
6 useClass: Http403Interceptor,
7 multi: true
8 }
9 ]
10 }).compileComponents()
11 );

The TestBed loads the HttpClientTestingModule, and sets up the 403 interceptor as a provider.

Let's use another beforeEach() block to populate our variables:

view plain print about
1beforeEach(() => {
2 httpClient = TestBed.inject(HttpClient);
3 httpMock = TestBed.inject(HttpTestingController);
4 interceptor = TestBed.inject(HTTP_INTERCEPTORS)[0] as Http403Interceptor
5 });

The httpClient and httpMock are both retrieved using the inject command on the TestBed. Both of these are imported from the use of the HttpClientTestingModule. The interceptor is retrieved a bit differently; retrieving the HTTP_INTERCEPTORS, which returns an array of all interceptors. We know our interceptor is the first--and only--one defined, so we get it by parsing the array returned.

Now, create test:

view plain print about
1it('should toggle errorOccured value when request status is 403', () => {
2}

When we name a test, I like to give details about the action and the condition.

Inside the test, let's start with an assertion to make sure our interceptor.errorOccured value is falsy:

Then, use the httpClient to start a request:

view plain print about
1httpClient.get(route ).subscribe( {
2 next: () =>
{},
3 error: (err: HttpErrorResponse) => {
4 expect(err.error).toEqual(error);
5 expect(interceptor.errorOccurred).toBeTruthy();
6 }
7 });

For this test, we only care about the error condition; not the success condition. We have two assertions, we verify that the errorOccurred value was toggled and that the error object is equal to our error object.

Finally, we want to create the HTTP Request, and trigger an error response:

view plain print about
1const req = httpMock.expectOne(route );
2 req.error(error, {status: 403});

Run the test and you'll see all success. Play with a sample project here