How to Test a Functional Interceptor in Angular

seanbh

seanbh

Posted on October 4, 2023

How to Test a Functional Interceptor in Angular


Photo by Ben Mullins on Unsplash

This post will build upon my previous one and show you how to test the functional interceptor that was implemented there:

import {
  HttpRequest,
  HttpInterceptorFn,
  HttpHandlerFn,
  HttpEvent,
} from '@angular/common/http';
import { Observable } from 'rxjs';

export const authInterceptor: HttpInterceptorFn = (
  request: HttpRequest<unknown>,
  next: HttpHandlerFn
): Observable<HttpEvent<unknown>> => {
  const clonedRequest = request.clone({
    setHeaders: {
      Authorization: 'Bearer [the token]',
    },
  });
  return next(clonedRequest);
};
Enter fullscreen mode Exit fullscreen mode

I am going to compare and contrast testing class-based and functional interceptors in this post, but feel free to skip straight to the functional interceptor test if that is all you are interested in.

Class-Based Test

A simple class-based test might look like this:

import { TestBed } from '@angular/core/testing';
import {
  HttpClientTestingModule,
  HttpTestingController,
} from '@angular/common/http/testing';
import { AuthInterceptor } from './auth.interceptor';
import { HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http';

describe('AuthInterceptor', () => {
  let httpTestingController: HttpTestingController;
  let httpClient: HttpClient;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [
        AuthInterceptor,
        { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
      ],
    });
    httpTestingController = TestBed.inject(HttpTestingController);
    httpClient = TestBed.inject(HttpClient);
  });

  afterEach(() => {
    httpTestingController.verify();
  });

  it('should add auth headers ', () => {
    //arrange
    const url = '/mockendpoint';

    //act
    httpClient.get(url).subscribe();

    // assert
    const req = httpTestingController.expectOne(url);
    expect(req.request.headers.get('Authorization')).toEqual(
      'Bearer [the token]'
    );
  });
});
Enter fullscreen mode Exit fullscreen mode

There is a fair bit of code here and I’ve gone over the details in a previous post and won’t repeat them here. The test itself is just making a (fake) HTTP request and then checking the header to verify that the token was added. It is the same regardless of whether the interceptor is class-based or functional. What does change is how we provide the interceptor.

Functional Test

Just like we did in the previous post, we need to switch to providing HttpClient using the standalone API, and then we can provide authInterceptor at the same time:

// removed the imports[]

providers: [
  provideHttpClient(withInterceptors([authInterceptor])),
  provideHttpClientTesting(),
],
Enter fullscreen mode Exit fullscreen mode

Notice that we have also included the provideHttpClientTesting function call and removed the imports array that contained HttpClientTestingModule.

Remember, the shift to standalone includes replacing modules with function calls. Just like provideHttpClient replaced importing the HttpClientModule, provideHttpClientTesting replaces importing the HttpClientTestingModule.

Here’s the diff between the class-based and functional test:


The code difference between the class-based and functional tests

Here is the test in full:

import { TestBed } from '@angular/core/testing';
import {
  HttpTestingController,
  provideHttpClientTesting,
} from '@angular/common/http/testing';
import {
  HttpClient,
  provideHttpClient,
  withInterceptors,
} from '@angular/common/http';
import { authInterceptor } from './auth.interceptor';

describe('AuthInterceptor', () => {
  let httpTestingController: HttpTestingController;
  let httpClient: HttpClient;

  beforeEach(() => {
    TestBed.configureTestingModule({
      // here are the KEY changes
      providers: [
        provideHttpClient(withInterceptors([authInterceptor])),
        provideHttpClientTesting(),
      ],
    });
    httpTestingController = TestBed.inject(HttpTestingController);
    httpClient = TestBed.inject(HttpClient);
  });

  afterEach(() => {
    httpTestingController.verify();
  });

  it('should add auth headers ', () => {
    //arrange
    const url = '/mockendpoint';

    //act
    httpClient.get(url).subscribe();

    // assert
    const req = httpTestingController.expectOne(url);
    expect(req.request.headers.get('Authorization')).toEqual(
      'Bearer [the token]'
    );
  });
});
Enter fullscreen mode Exit fullscreen mode

That’s it! I hope you found this useful.

Bibliography

💖 💪 🙅 🚩
seanbh
seanbh

Posted on October 4, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related