Issue with Angular/Jasmine: Undefined property 'pipe' not readable

I have been struggling to resolve a problem with Angular 9, Jasmine, and RxJS without much success. While my unit tests run successfully in Jasmine, there are certain lines of code that do not get executed.

Despite scouring multiple posts for assistance, none of the solutions provided seem to work for my specific issue, leaving me quite frustrated.

I am reaching out for help, please :)

CLASS TEST

describe('AuthRefreshAppInterceptor', () => {
    let accessToken: string;
    let endpoint: string;

    let authAppServiceMock: AuthAppService;
    let baseHTTPService: BaseHTTPService;
    let environmentServiceMock: EnvironmentService;
    let httpTestingController: HttpTestingController;
    let sessionStorageServiceMock: SessionStorageService;

    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [HttpClientTestingModule],
            providers: [
                CriptografiaService,
                BaseHTTPService,
                {
                    provide: AuthAppService,
                    useValue: jasmine.createSpyObj('AuthAppService', ['getNewAuth', 'saveAuth', 'createBodyAuth', 'createBodyAuth'])
                },
                {
                    provide: EnvironmentService,
                    useValue: EnvironmentMock
                },
                {
                    provide: HTTP_INTERCEPTORS,
                    useClass: AutorizacaoErroAppInterceptor,
                    multi: true,
                },
                {
                    provide: SessionStorageService,
                    useValue: jasmine.createSpyObj('SessionStorageService', ['saveItem', 'getItem', 'removeItem'])
                }
            ],
        });

        authAppServiceMock = TestBed.inject(AuthAppService);
        baseHTTPService = TestBed.inject(BaseHTTPService);
        environmentServiceMock = TestBed.inject(EnvironmentService);
        httpTestingController = TestBed.inject(HttpTestingController);
        sessionStorageServiceMock = TestBed.inject(SessionStorageService);

        accessToken = faker.finance.bitcoinAddress();
        endpoint = faker.internet.domainName();

        sessionStorageServiceMock.getItem = jasmine.createSpy()
            .and.returnValue({ access_token: accessToken });
    });

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

        authAppServiceMock.obterAutorizacaoApp = jasmine.createSpy()
            .and.returnValue(Observable.of({
                access_token: 'eyJhbGciOiJSUzI1NiIsImtpZCI6Ik5oMXY4YlZsODg2M3g3UnhWTlJhamciLCJ0eXAiOiJhdCtqd3QifQ.eyJuYmYiOjE1OTE3OTU5MjAsImV4cCI6MTU5MTc5OTUyRJhdnjh78sjiHDhdiajsd9sb2NhbGhvc3Q6NTUwMDEiLCJhdWQiOiJJZGVudGl0eVNlcnZlckFwaSIsImNsaWVudF9pZCI6IkFkbWluIiwic2NvcGUiOlsiSWRlbnR86d5F4dasdf.Gvxq02a2HaF4rpeB3KnBxRAHHa6WpU850V5377wpRBAA6rmw6PRzVRMnpd2mtr5CVY72NWCspsdU8a8XhwFTgoCDmjSlXKSZKmUGsEaCbXuzSQg7BwD7A9zul0U0VkbF1KTvLIdnmb1noyeOP04dDH',
                expires_in: 3600,
                token_type: 'Bearer',
                scope: 'IdentityServerApi'
              }));
    });

    it('Should execute a request GET and return a HttpErrorResponse with error 401 Unauthorized', () => {
        spyOn(baseHTTPService, 'httpGet').and.callThrough();

        baseHTTPService.httpGet(endpoint).subscribe({
            error: (httpErrorResponse: HttpErrorResponse) => {
                console.log(httpErrorResponse)
                expect(httpErrorResponse.status).toEqual(401);
                expect(httpErrorResponse.statusText).toEqual('Unauthorized');
                expect(httpErrorResponse.message).toContain('Http failure response for');
            }
        });

        const httpRequest = httpTestingController.expectOne(
            `${environmentServiceMock.apiGatewayURL}/${endpoint}`
        );
        httpRequest.flush(null, { status: 401, statusText: 'Unauthorized' });

        expect(baseHTTPService.httpGet).toHaveBeenCalledTimes(1);
        expect(httpRequest.request.url).toBe(`${environmentServiceMock.apiGatewayURL}/${endpoint}`);
        expect(httpRequest.request.method).toBe('GET');
        expect(httpRequest.request.body).toBeNull();
    });
    ...
});

INTERCEPTOR: TESTED CLASS

export class AuthRefreshAppInterceptor {
    ...
    private getNewAuth(request: HttpRequest<any>, next: HttpHandler) {
        return this.authAppService
            .getAuthApp()
            .pipe(
                // THIS BLOCK IS NOT TESTED
                switchMap((auth: AuthAppModel) => {
                    this.authAppService.saveAuth(auth);
                    return next.handle(this.addHeaders(request));
                }),
                // THIS BLOCK IS NOT TESTED
                catchError(() => next.handle(request))
            );
    }
    ...
}

SERVICE

export class AuthAppService {
    ...
    public salvarAutorizacao(auth: AuthAppModel) {
        this.sessionStorageService.removeItem(this.authKey);
        this.sessionStorageService.saveItem(this.authKey, auth);
    }
    
    public getAuthApp(): Observable<AuthAppModel> {
        return this.httpClient.post<AuthAppModel>(
            `${this.environmentService.apiGateway}/identity/token`,
            this.createBodyAuth(),
            { headers: this.createHeaders() }
        );
    }
    ...
}

Thank's

Answer №1

To ensure proper functionality, include getAuthApp as a method in the jasmine.createSpyObj

 {
   provide: AuthAppService,
   useValue: jasmine.createSpyObj('AuthAppService', ['getAuthApp' 'getNewAuth', 'saveAuth', 'createBodyAuth', 'createBodyAuth']) // add 'getAuthApp' here
 },

When testing the success path (switchMap)

import { of } from 'rxjs';
....
// In the beginning of your test, mock these functions
authAppServiceMock.getAuthApp.and.returnValue(of({})); // insert your mock value here
authAppServiceMock.saveAuth.and.returnValue(of({})); // insert your mock value here
... proceed with your test

When testing the fail path (catchError)

import { throwError } from 'rxjs';
....
// In the beginning of your test, mock this function
authAppServiceMock.getAuthApp.and.returnValue(throwError({})); // mock the throw error to trigger catchError
.... proceed with your test

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

Utilizing "regression-js" within an Angular 2 project: A comprehensive guide

I have integrated the Regression npm module https://www.npmjs.com/package/regression into my Angular 2 application to utilize the Linear Regression functionality. I installed the package using "npm install regression". However, I encountered errors while a ...

Challenges with Primeng Component UI

I am currently working with PrimeNG components, but I'm facing issues with the UI display on the web browser. At this moment, I need to showcase a static dropdown. I have referred to PrimeNG for guidance. Below is the code snippet to implement that c ...

Encountering an error during installation of typings

While attempting to run the Angular2 Quickstart Demo (https://angular.io/guide/quickstart), I encountered an issue at this specific point: typings install The error message I received is as follows: $ typings install typings ERR! message an HTTP(S) pro ...

ngx-emoji mart - The error message "Type 'string' is not assignable" is being displayed

While working on a project involving the @ctrl/ngx-emoji-mart package, I encountered a perplexing issue. The code functioned flawlessly in Stackblitz but when I attempted to run it on my local system, an error surfaced: Type 'string' is not assig ...

Difficulties Arising in Flex Layout Usage Due to ngFor Loop Implementation

Despite my efforts, I couldn't locate a question quite similar to mine. If an answer already exists somewhere, I apologize. I'm trying to create a simple flex layout in row format where 2 containers are placed side by side. Inside another compon ...

Testing Vue.js components - monitoring changes to props

I'm currently facing an issue while trying to unit test a watcher on a prop within a Vue component using Karma+Jasmine. It appears that the watchers on props are not being triggered during the unit test. Let's take a look at a simple example com ...

When executing the command "ng serve" in Angular, the following error is displayed: "webpack: Compilation failed."

I am currently using Mac OS (10.11.6) and have been attempting to familiarize myself with Angular. I successfully installed node.js, typescript, and angular-cli, and everything seemed to be functioning properly. However, whenever I executed "ng serve", it ...

JavaScript - Imported function yields varied outcome from module

I have a utility function in my codebase that helps parse URL query parameters, and it is located within my `utils` package. Here is the code snippet for the function: export function urlQueryParamParser(params: URLSearchParams) { const output:any = {}; ...

How can I effectively utilize the Angular router to share routes among various named outlets?

My application requires displaying the same components or routes in multiple areas of the interface. This means allowing users to show certain components in various locations based on their selection, such as side panels or bottom right panels. In my situ ...

Material-UI chart displays only loading lines upon hovering

Currently, I am utilizing a Material UI Line chart in my NextJS 14 application to showcase some data. Although the data is being displayed properly, I have encountered an issue where the lines on the chart only render when hovered over, rather than appeari ...

Can someone please explain how I can extract and display information from a database in separate text boxes using Angular?

Working with two textboxes named AuthorizeRep1Fname and AuthorizeRep1Lname, I am combining them in typescript before storing them as AuthorizeRep1Name in the database. Refer to the image below for the result. This process is used to register and merge the ...

Converting an array into an object by using a shared property in each element of the array as the key

I have an item that looks like this: const obj = [ { link: "/home", title: "Home1" }, { link: "/about", title: "About2" }, { link: "/contact", title: "Contact1" } ] as const and I want to p ...

Angular parent component struggles to manage control over multiple child components

As a newcomer to Angular, I have a question regarding my Page1 setup. Page1 has two menu options at the top: when the first option is clicked, Page1.html is displayed, and clicking the second menu displays another component (Page2) in the context area. I w ...

Learn how to easily toggle table column text visibility with a simple click

I have a working Angular 9 application where I've implemented a custom table to showcase the data. Upon clicking on a column, it triggers a custom modal dialog. The unique feature of my setup is that multiple dialog modals can be opened simultaneously ...

Using JavaScript/TypeScript to sort through and separate two arrays

Creating a list of checkboxes on a UI allows users to toggle and filter data results. To achieve this, I am storing the selected checkboxes as a string array. The structure of my code is outlined below: export interface IMyObjectFromAPI { status: { ...

Issues arise when attempting to extract data from a data provider using JSON within the context of the Ionic framework

Hey there! I'm relatively new to the world of Angular and Ionic, and I've embarked on a project to create a pokedex app. My approach involves using a JSON file containing an array of "pocket monsters". However, my current challenge lies in extrac ...

Encountered an issue while processing the firebase get request with the following error message: 'Unauthorized request in Angular'

Here are my rules: Utilizing a Firebase database Calling an API URL from this section https://i.stack.imgur.com/auAmd.png I am attempting to retrieve data from a Firebase API in an Angular application, but I keep encountering an 'Unauthorized reque ...

What sets apart window.location.href from this.router.url?

I'm curious about the various methods of obtaining the current URL in Angular. For instance: this.router.url My main question is: What advantages does using this.router.url offer over simply using window.location? Could someone kindly provide an exp ...

The function slice is not a method of _co

I'm attempting to showcase the failedjobs array<any> data in a reverse order <ion-item *ngFor="let failjob of failedjobs.slice().reverse()"> An issue arises as I encounter this error ERROR TypeError: _co.failedjobs.slice is not a fu ...

What is the best way to determine in component.html whether the column type is equal to 1 to show the label text "Active,"

Having trouble checking the value of an object named ReportControl. If the column type is 1, display the label "active"; otherwise, display the label "not active" on reportcomponent.html. The data for the ReportControl object is as follows: {"reportId": ...