Leverage Jasmine in Angular for spying on nested methods in HttpClient

I am facing a situation in which I need to monitor a method that is invoked after another method is triggered.

Here is the class/method I am testing:

@Injectable()
export class SomeService {

  constructor(private customHttpClient: CustomHttpClient) {
  }

    updateSomethingCool(signerVO: SignerVO): Observable<SignerVO> {

    // ...

    return this.customHttpClient.withCustomOverrides(new CustomErrorHandlerHttpInterceptorOverride({ passthroughStatusCodes: [BAD_REQUEST, BAD_GATEWAY] }))
        .put<SignerVO>(`/my/url/goes/here`, signerVO);
    }
}

This class uses CustomHttpClient which has the following structure:

    @Injectable()
    export class CustomHttpClient extends HttpClient {
        private interceptors: HttpInterceptor[] | null = null;

        constructor(private injector: Injector,
            originalHandler: HttpHandler, private originalBackend: HttpBackend) {
            super(originalHandler);
        }

        public withCustomOverrides(...overrides: CustomHttpInterceptorOverride[]): HttpClient {

            // do very customizable things here

            return new CustomDelegatingHttpClient(
                new CustomHttpInterceptingHandler(this.originalBackend, this.interceptors, overrides));
        }
    }

    export class CustomDelegatingHttpClient extends HttpClient {
        constructor(private delegate: HttpHandler) {
            super(delegate);
        }
}

Below is my approach to performing unit testing on the put method to ensure it has been called. This requires me to spy on the put method:

describe(SomeService.name, () => {
let service: SomeService;
let customHttpClient: CustomHttpClient;

let emptySignerVO: SignerVO = new SignerVO();

beforeEach(() => {
    customHttpClient= <CustomHttpClient>{};
    customHttpClient.put = () => null;
    customHttpClient.withCustomOverrides = () => null;

    service = new SomeService(customHttpClient);
});

describe('updateSomethingCool', () => {

    it('calls put', () => {
        spyOn(customHttpClient, 'put').and.stub();

        service.updateSomethingCool(emptySignerVO);

        expect(customHttpClient.put).toHaveBeenCalled();
    });
});

Upon running the test, I encounter the following failure message:

TypeError: Cannot read property 'put' of null

However, I am unsure how to properly define the put or withCustomOverrides methods within the beforeEach section of the test.

Note that CustomHttpClient serves as an enhanced wrapper class around Angular's HttpClient offering additional functionalities.

Your assistance is greatly appreciated!

Answer №1

My recommendation is to incorporate httpClient :

withinStart((http: HttpClient) => {
  httpClient = http;
  httpClient.put = () => null;
  httpClient.withCustomOverrides = () => null;
  service = new CustomService(log, httpClient);});

Answer №2

Are you leveraging Angular's dependency injection to inject HttpClient into CustomHttpClient? When testing services that rely on the HttpClient, you can use the HttpTestingController. Here is an example of how this might be implemented in a service unit test:

it(`should retrieve a new data instance of type T`, 
  async(inject([TestDataService, HttpTestingController],
  (service: TestDataService, backend: HttpTestingController) => {
    // ... tests go here
  })
));

Answer №3

It turns out I was pretty close to the correct solution after all. The test code itself was accurate; however, there was a necessary update needed in the beforeEach() method:

beforeEach(() => {
    customHttpClient = <CustomHttpClient>{};
    customHttpClient.put = () => null;
    customHttpClient.withCustomOverrides = () => customHttpClient;

    service = new SomeService(customHttpClient);
});

All that was required was assigning the customHttpClient object to the .withCustomOverides method.

In retrospect, this change aligns with the method chaining structure in the actual function call.

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

Retrieving data from a nested JSON array using AngularJS

I am struggling to navigate the intricate nested tree view of child items within a JSON array. I have been grappling with this challenge for days, trying to figure out how to access multiple children from the complex JSON structure. Can someone provide g ...

Fetch information from the themoviedb API

My goal is to retrieve all movies from the themoviedb API, and I attempted to do so by using the popular movies option. The default setting returns 20 movies per page. Below is the route I created for fetching popular movies: /* GET movies listing. */ r ...

How to disregard the "Declaration not found" error while using VS Code with TypeScript

Working with Typescript and Vue Cli in VS Code, I encountered a "definition missing warning" at this particular line: import { setupCalendar, DatePicker } from "v-calendar"; The issue states: Could not find a declaration file for module 'v-calen ...

What can cause a problem with the reduce function that populates an empty object with keys in TypeScript?

I've encountered an issue with a function that is meant to reduce an object. The problem lies in using the reduce method to assign the values of acc[key] as object[key], which is resulting in errors in the code. I am trying to avoid using any specific ...

Convert individual packages within the node_modules directory to ES5 syntax

I am currently working on an Angular 12 project that needs to be compatible with Internet Explorer. Some of the dependencies in my node_modules folder are non es5. As far as I know, tsc does not affect node_modules and starts evaluating from the main opti ...

The highlighted red lines in Visual Studio Code

I am facing an issue with some red squiggles in my code: https://i.sstatic.net/UBmgi.jpg To address this, I have declared variables like this: import SearchFilterViewModel = SearchFilter.SearchFilterViewModel; import SearchResultsViewModel = SearchResul ...

The HttpInterceptor is programmed to identify and capture 401 error responses

After successfully implementing a code that called a logout() method upon receiving a 401 response from the server, I encountered issues following an upgrade of Angular from 5.2 to 7.0.3. It seems like either the HttpInterceptor interface has been modified ...

Ever since the update to Angular 16, ngx-virtual-scroll has been experiencing issues and is

After updating to Angular 16, ngx-virtual-scroll seems to be malfunctioning and throws an error in the terminal. The error message reads: 'VirtualScrollerModule' does not appear to be an NgModule class. This indicates that the library (ngx-virtu ...

Typescript allows for the creation of a static class that can be accessed without needing to instantiate it

My goal is to design a TypeScript class that can be accessed directly without the need to create new instances. This is necessary because some classes will modify the variables in this shared class, while others must reference those changes. Below is the ...

Exploring the MVVM architecture in React and the common warning about a missing dependency in the useEffect hook

I'm currently in the process of developing a React application using a View/ViewModel architecture. In this setup, the viewModel takes on the responsibility of fetching data and providing data along with getter functions to the View. export default f ...

Having trouble getting JSX to compile while running tests with Jasmine and PhantomJS

When attempting to write a test that includes a rendering of a component using react/jasmine/phantomjs, I encountered an issue. My approach was as follows: beforeEach(function(){ var component = TestUtils.renderIntoDocument( <JsonTab ...

Pagination Component for Angular Material

My question is very specific, and I am hoping for an equally specific answer. I have searched online, but the Angular docs are not very helpful for real-life scenarios. It feels like trying to find a needle in the ocean. Oh Angular docs... This is what I ...

How can you retrieve the user that is currently signed in using AngularFire and Firebase Authentication in Angular?

If you're working with Angular and trying to access the currently signed-in user through Firebase Auth, you may encounter some difficulties. Here's a snippet of code provided in the Firebase Auth documentation that demonstrates how to get the sig ...

Removing the outer array of objects can be achieved by using a variety of

My goal was to eliminate the outer array of objects. I attempted to achieve this by using the code below: export class AppComponent implements OnInit { name = 'Angular'; EmployeeData=[ {"name": [{ "grade": &quo ...

Ways to handle route initialization in Angular 13 when starting the app?

What is the most effective way to handle route resolution before components are loaded? I want to avoid using guards for every single route, and I need to load a component on the '/' path. Using a parent '/' path without a component but ...

Validation of emails in Angular through the utilization of reactive forms

Hello there, I'm new to Angular and looking for some assistance. Specifically, I am currently working on validating an email input using a reactive form with the keyup event. registerform:any; ngOnInit(): void { this.registerform = new F ...

An error occurred within Angular 8 while attempting an HTTP post request, as it was unable to access the 'message' property of

After conducting a search on Stack Overflow, I found a similar question that relates to my issue. Login OTP Component: onSubmitValidOTP() { this.authenticationService.ValidOTP(this.fo.OtpControl.value, username, role) .pipe(first ...

Exploring TypeScript methods for manipulating Apollo Client query results that have been transformed into props

After following a blog tutorial on GraphQL, I successfully integrated my GraphQL query with a React component to display the returned results. Everything seemed to be working fine as I could log this.props. However, when I tried to access individual data ...

The latest version of Angular2 has not been installed correctly

I decided to dive into the world of Angular2 and Express by cloning the repository from https://github.com/vladotesanovic/angular2-express-starter for my project. My goal was to start at a "Hello World" level, so I made it simpler by deleting all the file ...

Working with Angular 6 and Electron: The limitations of using a mocked class for unit testing and extending the real class

I encountered a problem while trying to write a unit test for my Electron app using Jasmine and Angular 6. The issue arises from the fact that I have a service which is not required to be tested in the specific test scenario of another service. To handle t ...