Testing Angular Http Interceptors

Paul Halliday

HttpInterceptors allow us to modify HTTP requests within our application. A common use case is to add an Authorization header to each request. This article looks at testing this integration.

Interceptors are part of Angular's HttpClient module, which was introduced with Angular 4.3. If this is new to you, you can get an overview here.

Service

We’ll be using a simple service that gets Posts from a REST API. Using our imagination, we’ll pretend that this data requires authorization and we’ll create a HttpInterceptor to inject the header for each request.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Post } from '../models/user.model';

@Injectable()
export class DataService {
  ROOT_URL = `http://jsonplaceholder.typicode.com`;

  constructor(private http: HttpClient) {}

  getPosts() {
    return this.http.get<Post[]>(`${this.ROOT_URL}/posts`);
  }
}

Interceptor

If you’ve never used a HttpInterceptor before, check out this article. Our interceptor looks like this:

import { Injectable } from '@angular/core';
import {
  HttpInterceptor,
  HttpEvent,
  HttpHandler,
  HttpRequest,
} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class AuthHttpInterceptor implements HttpInterceptor {
  intercept(
    req: HttpRequest<any>,
    next: HttpHandler,
  ): Observable<HttpEvent<any>> {
    const newRequest = req.clone({
      headers: req.headers.set('Authorization', 'token YOUR-TOKEN-HERE'),
    });
    return next.handle(newRequest);
  }
}

Testing Authorization Header

We want to test that the appropriate header is added to each request. Let’s create a spec file that looks at this.

import { TestBed } from '@angular/core/testing';
import {
  HttpClientTestingModule,
  HttpTestingController,
} from '@angular/common/http/testing';
import { DataService } from './data.service';
import { AuthHttpInterceptor } from './data.interceptor';
import { HTTP_INTERCEPTORS } from '@angular/common/http';

describe(`AuthHttpInterceptor`, () => {
  let service: DataService;
  let httpMock: HttpTestingController;

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

    service = TestBed.get(DataService);
    httpMock = TestBed.get(HttpTestingController);
  });
});

The beforeEach function is ran before every test, allowing us to configure a new TestBed module that registers both the DataService and the AuthHttpInterceptor.

We’ve assigned the injected version(s) of these providers into the service and httpMock variables so that we can use them throughout our test suite.

Let’s start off by ensuring we have an Authorization header:

it('should add an Authorization header', () => {
  service.getPosts().subscribe(response => {
    expect(response).toBeTruthy();
  });

  const httpRequest = httpMock.expectOne(`${service.ROOT_URL}/posts`);

  expect(httpRequest.request.headers.has('Authorization')).toEqual(true);
});

Observables don’t fire unless we subscribe to them, so by subscribing to the getPosts function we’re sending a HTTP GET request. As we’re not concerned with the results of the request (i.e. whether it correctly returned posts) we can simply check that the request happened.

Next, we can assert that the httpRequest’s request.headers object contains a header with the name of Authorization. If this is the case, then our interceptor has successfully added it.

We can then check to see whether the included Authorization header contains the token we expect. You can either make another it block for this, or add it inside the previous one:

expect(httpRequest.request.headers.get('Authorization')).toBe(
  'token YOUR-TOKEN-HERE',
);

This matches the token we set inside of our interceptor.

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#