angular 6: Get HTTP Interceptor Instance in Test

Testing HTTP interceptors in angular is a tricky thing. There are thousands of manuals on the web, so this article will not cover the overall “how to” for testing HTTP interceptors in angular. The article focuses on how to access the interceptors’ instance itself.

This may be useful to mock methods in the interceptor itself like p.e. a status code matcher. To mock this, you need to access the instance (the real used one injected as provider).

Recap: How are interceptors added to angular application?

One or more interceptors are added through the providers array of your module. Angular additionally adds his own one.

export class MyModule {
  static forRoot(): ModuleWithProviders {
    return {
      ngModule: MyModule,
      providers: [
        // ...
        { provide: HTTP_INTERCEPTORS, useClass: MyInterceptor, multi: true }
      ]
    };
  }
}

How to get Interceptors Instance in Test

  • Add the interceptor to the providers’ array of your TestBed:
TestBed.configureTestingModule({
  imports: [
    HttpClientTestingModule
    // ...
  ],
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: MyInterceptor, multi: true }
  ]
});

Providing the interceptor itself directly without HTTP_INTERCEPTOR will cause an extra instance as it is angular DI by design.

  • You can now inject HTTP_INTERCEPTORS into your test:
it("should ...", inject(
  [HTTP_INTERCEPTORS],
  (interceptors: HTTP_INTERCEPTORS) => {}
));
  • When debugging the interceptors, you can see, that there are two interceptors provided by this injection token: Injection token screenshot

  • To get the instance you need, you may iterate over the list and compare the instances with your one:

interceptors.forEach((interceptor: HttpInterceptor) => {
  if (interceptor instanceof MyInterceptor) {
    // this is your interceptor instance now!
  }
});

Optimization

Fast access to the instance in each test

If you need to get the interceptors instance you may set the reference to the instance in beforeEach function of your tests. Then you can use the reference in your test descriptions as displayed:

let interceptorInstance: HttpInterceptor;
beforeEach(() => {
  TestBed.configureTestingModule({
    imports: [
      HttpClientTestingModule
      // ...
    ],
    providers: [
      { provide: HTTP_INTERCEPTORS, useClass: MyInterceptor, multi: true }
    ]
  });
  interceptorInstance = getInterceptorInstance<MyInterceptor>(
    TestBed.get(HTTP_INTERCEPTORS),
    MyInterceptor
  );
});

it("should test if interceptor instance defined", () => {
  expect(interceptorInstance).toBeDefined();
});

Generic method to retrieve interceptor instance

// type should be of type typeof T but this causes "error TS2693: 'T' only refers to a type, but is being used as a value here",
// because the instance i not available during compile time --> using any to avoid issue
getInterceptorInstance<T extends HttpInterceptor>(interceptors: HttpInterceptor[], type: any): HttpInterceptor {
    let searchedInterceptor: HttpInterceptor = null;
    interceptors.forEach((interceptor: HttpInterceptor) => {
        if (interceptor instanceof type) {
            searchedInterceptor = interceptor;
        }
    });
    return searchedInterceptor;
}