Having difficulties testing the Angular HTTP interceptor with Karma and Jasmine

Testing an http interceptor has been quite the challenge for me. Despite having my token logged in the console and ensuring that the request URL matches, I still can't figure out why it's not working with the interceptor in place. Interestingly, all my other http services pass without any issues when tested using the same method.

The interceptor class below adds the bearer token to every request successfully. It relies on the Ionic Platform native class, which is mocked during testing:

@Injectable()
export class AddHeaderInterceptor implements HttpInterceptor {

  public constructor(
      private auth: AuthService, private platform: Platform, 
      private config: ConfigurationService
  ) {}

  public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    console.debug('req', req.url);
    return Observable.fromPromise(this.platform.ready())
      .switchMap(() => Observable.fromPromise(this.auth.getToken()))
      .switchMap((tokens: TokenCacheItem[]) => {
        const baseUrl = new RegExp(this.config.getValue('baseUrl'), 'g');
        if (! req.url.match(baseUrl)) {
          return Observable.of(null);
        }
      
     

The error message "Expected one matching request" keeps popping up no matter what I try. Even when I spy on the `getToken` method from the auth service, it never gets called. This issue persists whether I use absolute or relative URLs in `http.expectOne()`. Any suggestions on how to fix this?

Answer №1

By utilizing the karma done function and directly creating the Addheader object, I was able to find a solution:

fdescribe('AddHeaderInterceptor Service', () => {

  let http: HttpTestingController;
  let httpClient: HttpClient;
  let auth: AuthService;
  let logger: LogService;
  let config: ConfigurationService;
  let platform: Platform;
  let interceptor: AddHeaderInterceptor;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [
        ConfigurationService,
        {
          provide: HTTP_INTERCEPTORS,
          useClass: AddHeaderInterceptor,
          multi: true,
        },
        { provide: Platform, useClass: PlatformMocked },
        { provide: LogService, useClass: LogServiceMock },
        { provide: AuthService, useClass: AuthServiceMock }
      ],
    });
    config = TestBed.get(ConfigurationService);
    auth = TestBed.get(AuthService);
    logger = TestBed.get(LogService);
    platform = TestBed.get(Platform);
    httpClient = TestBed.get(HttpClient);
    http = TestBed.get(HttpTestingController);
    interceptor = new AddHeaderInterceptor(logger, auth, platform, config);
  });

  fit('should add header to request', (done) => {
    expect((interceptor as any) instanceof AddHeaderInterceptor).toBeTruthy();
    const next: any = {
      handle: (request: HttpRequest<any>) => {
        expect(request.headers.has('Authorization')).toBeTruthy();
        expect(request.headers.get('Authorization')).toEqual('Bearer TESTO');
        return Observable.of({ hello: 'world' });
      }
    };
    const req = new HttpRequest<any>('GET', config.getValue('baseUrl') + 'test');
    interceptor.intercept(req, next).subscribe(obj => done());
  });

});

Answer №2

Encountered a similar situation while implementing the same fromPromise -> switchMap pattern in my interceptor. Testing seemed to be impacted by this, even though the code functioned correctly without any issues. Surprisingly, following your testing solution resolved the issue for me.

The reason behind this behavior remains unknown, but I am grateful for your helpful solution!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring the world of unit testing in aws-cdk using TypeScript

Being a newcomer to aws-cdk, I have recently put together a stack consisting of a kinesis firehose, elastic search, lambda, S3 bucket, and various roles as needed. Now, my next step is to test my code locally. While I found some sample codes, they did not ...

Keep track of the input values by storing them in an array after they are

Creating an Angular application to read barcodes. barcode-scanner.component.html <form #f="ngForm" class="mt-3 text-center" id="myform" (ngSubmit)="onSubmit(f)"> <div class="text-center"> <input type="text" maxlength= ...

What is the best way to change the number 123456789 to look like 123***789 using either typescript or

Is there a way to convert this scenario? I am working on a project where the userID needs to be displayed in a specific format. The first 3 characters/numbers and last 3 characters/numbers should be visible, while the middle part should be replaced with ...

When setting up Webpack with TypeScript, an error is encountered related to imports

Recently, I attempted to convert my Webpack configuration from JavaScript to TypeScript but encountered numerous difficulties in the process. To kick things off, I created a basic webpack configuration file with some parts missing. Here is how my webpack.c ...

How can I exclude *.d.ts files from tslint checking?

Recently, I decided to integrate tslint into my workflow. Following the installation steps, I used the command: npm install tslint tslint-config-ms-recommended --save-dev My configuration file tslint.json now looks like this: { "extends": "tslint-co ...

Tips for fixing type declaration in a generic interface

Here is a simple function that constructs a tree structure. interface CommonItem { id: string parent: string | null } interface CommonTreeItem { children: CommonTreeItem[] } export const generateTree = <Item extends CommonItem, TreeItem extends ...

What is the best way to execute my mocha fixtures with TypeScript?

I am seeking a cleaner way to close my server connection after each test using ExpressJS, TypeScript, and Mocha. While I know I can manually add the server closing code in each test file like this: this.afterAll(function () { server.close(); ...

Retrieve an item from an Angular 2 Observable Dataservice

I'm new to using Observables in Angular 2 and I have a query. In my data service class, there is a method called getExplorerPageData which sends an http request returning a data structure containing several arrays. I want to create another function n ...

Typescript: Securing Data with the Crypto Module

I am currently working on encrypting a password using the built-in crypto module. Previously, I used createCipher which is now deprecated. I am wondering if there is still an effective way to achieve this. Here is the old code snippet: hashPassword(pass: ...

What is the best way to save data generated in Angular to a file using Node.js?

After researching, I have discovered the method for saving data in a file using node.js save(): void { this.modeler.saveXML((err: any, xml: any) => console.log('Result of saving XML: ', err, xml)); } } ...

What is the best way to organize information in a table based on the date

This is my data table https://i.stack.imgur.com/1DNlj.png in the displayed table, the registration dates are sorted with the oldest date at the top. However, I aim to have the newest data displayed first. Below is the code snippet I am using: this.dataSo ...

In TypeScript, Firestore withConverter may return a QueryDocumentSnapshot instead of the expected Class object

I'm currently exploring the usage of Firestore's withConverted method in Typescript to retrieve queries as instances of my customized class. Custom EventConverter Class import Event from "@/models/Event"; class EventConverter implemen ...

Warning for unmet peer dependency still persisting despite installation of the required peer dependency

Adding redux to my Ionic 3+ application has been a bit of a challenge. Here's the command I used: npm i redux @angular-redux/store flux-standard-action redux-logger --save After running this command, I encountered the following error message: UNMET ...

Encountering an error with Dynamic Control generic react-hook-form: Error code TS2322 appears - Type 'Control<FormFields, any>' cannot be assigned to type 'Control<FieldValues, any>'

Within my application, I am utilizing react-hook-form in conjunction with the latest version of MUI 5.11. I have developed a reusable Select component: ...someImports import { Control, Controller } from 'react-hook-form'; interface SelectProps { ...

NPM displaying an error message

npm ERROR! Windows_NT 6.3.9600 npm ERROR! Command "C:\\Program Files (x86)\\nodejs\\\\node.exe" "C:\\Program Files (x86)\\nodejs\\node_modules\\npm\\bin\\np ...

"Although TypeOrm successfully generates the database, there seems to be a connectivity issue

Attempting to set up a JWT authentication system using NestJs and SQLite. The code successfully generates the SQLite file, but then throws an error stating "Unable to connect to the database." Upon checking with the SQLite terminal, it became apparent that ...

Error TS2488 in React TypeScript: The data type 'IStateTypes' is required to have a method called '[Symbol.iterator]()' that returns an iterator

At the moment, I am working on implementing a global state in React Hooks but have run into an issue. https://i.stack.imgur.com/DN83K.png The current problem I'm facing is with [Symbol.iterator](. I am uncertain about how to resolve this as I am in ...

`Incorporating CSS Pseudo-elements to Customize Angular Font Awesome Icons Issue`

I successfully integrated @fortawesome/angular-fontawesome into my Angular 7 application by following the instructions provided on https://www.npmjs.com/package/@fortawesome/angular-fontawesome. Although I am able to use the icons directly, I am facing di ...

How to use Web3 in conjunction with a Paymaster to execute a smart contract transaction and have the Paymaster cover the gas fees?

Seeking a method to call a smart contract function using web3js in an Angular 17 project, with gas fees covered by a paymaster-contract. How can I make this happen? web3 version: 4.6.0 Angular Version: 17 network: zkSync const web3 = new Web3(window ...

Ways to emit a value from an observable once it is defined?

Is it feasible to create an observable that can wrap around the sessionStorage.getItem('currentuser')? This way, calling code can subscribe and retrieve the value only after it has been set in the sessionStorage. Do you think this is achievable? ...