A guide on simulating HTTP methods in Jest when dealing with private methods

I'm grappling with how to simulate the following functionality. I need to simulate both methods: getAllBookInCategory, deleteBookInCategory

The public method invokes private methods and I presume I don't need to test private methods, only calling public methods and confirming that private methods are called. Is my understanding correct?

public methods: getAllBookInCategory, deleteBookInCategory

private method: makeRequest

import rp from "request-promise-native";

export class BookService {

@param bookCategory id of book category
@param boooks list of books

public static async getAllBookInCategory(bookCategory: string) {

        try {
            const neededInfo = {
                url: `https://${process.env.BOOK_HOST}/bookapi/${process.env.BOOKAPI_VERSION}/iterative/bookCategory/${ bookCategory }/books/all `,
                method: 'GET',
            }

            const result = await BookService.makeRequest(bookCategory, neededInfo);

            return rp(result);
        } catch(error) {
            Console.log(`Failed to get All Books in given category ${error}`)
        }
    }



public static async deleteBookInCategory(bookCategory: string, books: string[]) {

        try{
            const neededInfo = {
                url: `https://${process.env.BOOK_HOST}/bookapi/${process.env.BOOKAPI_VERSION}/ iterative /bookCategory/${ bookCategory }/books/bookDelete?books=${books.join()}`,
                method: 'DELETE',
            }

            const result = await BookService.makeRequest(bookCategory, neededInfo);

            return rp(result);
        } catch(error) {
            Console.log(`Failed to delete books from category: ${error}`)
        }
    }




private static async makeRequest(bookCategory: string, neededInfo: any, bodydata?: any) {

        const authValue = await BookService.getAuthValue(bookCategory, neededInfo);

        return {
            method: neededInfo.method,
            url: neededInfo.url,
            headers: {
                Host: process.env.BOOK_HOST,
                Authorization: authValue,

            },
            body: bodydata,
            json: true
        };
    }
}

Answer №1

Below is the solution for testing the getAllBookInCategory method. There is a specific issue in your code that needs to be corrected. Instead of using return rp(result), you should use return await rp(result).

BookService.ts:

import rp from 'request-promise-native';

export class BookService {
  public static async getAllBookInCategory(bookCategory: string) {
    try {
      const neededInfo = {
        url: `https://${process.env.BOOK_HOST}/bookapi/${process.env.BOOKAPI_VERSION}/iterative/bookCategory/${bookCategory}/books/all`,
        method: 'GET',
      };

      const result = await BookService.makeRequest(bookCategory, neededInfo);

      return await rp(result);
    } catch (error) {
      console.log(`Failed to get All Books in given category ${error}`);
    }
  }

  public static async deleteBookInCategory(bookCategory: string, books: string[]) {
    try {
      const neededInfo = {
        url: `https://${process.env.BOOK_HOST}/bookapi/${
          process.env.BOOKAPI_VERSION
        }/iterative/bookCategory/${bookCategory}/books/bookDelete?books=${books.join()}`,
        method: 'DELETE',
      };

      const result = await BookService.makeRequest(bookCategory, neededInfo);

      return rp(result);
    } catch (error) {
      console.log(`Failed to delete books from category: ${error}`);
    }
  }

  private static async makeRequest(bookCategory: string, neededInfo: any, bodydata?: any) {
    const authValue = await BookService.getAuthValue(bookCategory, neededInfo);

    return {
      method: neededInfo.method,
      url: neededInfo.url,
      headers: {
        Host: process.env.BOOK_HOST,
        Authorization: authValue,
      },
      body: bodydata,
      json: true,
    };
  }

  private static async getAuthValue(catetory, info) {
    return 'authvalue';
  }
}

BookService.test.ts:

import { BookService } from './BookService';
import rp from 'request-promise-native';

jest.mock('request-promise-native', () => jest.fn());

describe('BookService', () => {
  afterEach(() => {
    jest.resetAllMocks();
    jest.restoreAllMocks();
  });
  describe('#getAllBookInCategory', () => {
    beforeEach(() => {
      process.env.BOOK_HOST = 'example.com';
      process.env.BOOKAPI_VERSION = 'v1';
    });
    afterEach(() => {
      process.env.BOOK_HOST = '';
      process.env.BOOKAPI_VERSION = '';
    });
    it('should make request correctly', async () => {
      const mAuthvalue = 'mocked auth value';
      jest.spyOn(BookService as any, 'getAuthValue').mockResolvedValueOnce(mAuthvalue);
      const mResponse = 'success';
      rp.mockResolvedValueOnce(mResponse);
      const actual = await BookService.getAllBookInCategory('programming');
      expect(actual).toEqual(mResponse);
      expect(rp).toBeCalledWith({
        method: 'GET',
        url: 'https://example.com/bookapi/v1/iterative/bookCategory/programming/books/all',
        headers: {
          Host: 'example.com',
          Authorization: mAuthvalue,
        },
        body: undefined,
        json: true,
      });
    });

    it('should print an error when make request failed', async () => {
      const mAuthvalue = 'mocked auth value';
      jest.spyOn(BookService as any, 'getAuthValue').mockResolvedValueOnce(mAuthvalue);
      const logSpy = jest.spyOn(console, 'log');
      const mError = new Error('Internal server error');
      rp.mockRejectedValueOnce(mError);
      await BookService.getAllBookInCategory('programming');
      expect(rp).toBeCalledWith({
        method: 'GET',
        url: 'https://example.com/bookapi/v1/iterative/bookCategory/programming/books/all',
        headers: {
          Host: 'example.com',
          Authorization: mAuthvalue,
        },
        body: undefined,
        json: true,
      });
      expect(logSpy).toBeCalledWith(`Failed to get All Books in given category ${mError}`);
    });
  });
});

Testing the deleteBookInCategory method can be done in a similar way.

For more details, you can check out the source code here.

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

Restricting zooming to only occur within the img element in a UI

Is there a method to enable image zoom inside specific divs without affecting the overall page zoom? At the moment, I have: <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale ...

Having constant problems with ngModel twoway binding. Any suggestions on how to successfully bind to a property in order to update an api link?

I am attempting to implement two-way binding in order to dynamically change the API endpoint when a button is clicked. The value attribute of the button should be used as part of the API URL string. I tried following an example in the Hero Angular App, bu ...

Struggling to configure Velocity Js?

After attempting to use Velocity Js for the first time, I encountered some issues while trying to create a navigation menu. Despite having functioning code and animations, the menu failed to open. I followed the instructions on the Velocity JS site by incl ...

Despite being logged, the current value of firebase.auth().currentUser in Firebase is null

I have coded a query in my service.TS file that displays the "state" of items based on the UID of the logged-in user: getLists(): FirebaseListObservable<any> { firebase.auth().onAuthStateChanged(function(user) { if (user) {console.log("blah", fir ...

Even when the outcome is not what was anticipated, mocha chai still manages to ace the examination

When testing my REST API response with mocha, chai, and express, I set the expected status to 201 but unexpectedly got a passing test result it('janus post', () => { request('https://***') .post('/***') .attach(&a ...

Memory leaks are occurring due to the texture from the video tag

Currently, I am working on creating a texture in THREE.js (r78) from a video tag and updating the texture 60 times per second by setting needsupdate=true in requestanimationframe. However, I am facing a problem where I notice memory leakage in the Chrome T ...

TypeScript allows for the declaration of a function that includes a mandatory property within the function signature

If I desire a tagged function with an interface such as: interface TaggedFun { (args): void; tag: boolean; } It appears that declaring a function to match this signature is not possible (since any function literal will lack the mandatory tag prop ...

Tips for effectively sifting through your news feed

I have developed a Chrome extension that extracts newsfeeds from social media pages. However, I want to ensure that posts from specific social media accounts that users follow are kept without injecting but rather filtering them. The challenge lies in the ...

Discover the way to utilize the java enum toString() function in jQuery

In my Java Enum class called NciTaskType, I have defined two tasks: Pnd Review Woli and Osp Planning. public enum NciTaskType { PndReviewWoli, // 0 OspPlanning, // 1 ; @Override public String toString() { switch (this) ...

Switch between two fields using just one animation

I've been experimenting with creating an information tag that slides out from behind a circular picture. To achieve this effect, I created a block and circle to serve as the information field placed behind the image. However, I'm facing a challe ...

How is it possible that the SetTimeout code continues to run even after calling clearTimeout

I have this code snippet within my react component: //some code var timeout = null; //global variable //some more code useEffect(() => { if(...some condition) { console.log('test1'); timeout = setTimeout(() => { ...

Navigate to Element ID with Nuxt.js on click

Currently, I am in the process of developing a Nuxt application and facing a challenge. I want the page to automatically scroll down to a specific element after clicking on a button. Interestingly, the button and the desired element are located within diff ...

Instructions for adding a select dropdown feature in Angular 6 that includes a search filter. Additionally, tips on how to filter objects by their name property

I need to add an auto-complete feature in my Angular 6 app where the data is displayed as objects in a dropdown and filtered as we type. **template.html** <mat-form-field > <input matInput [matAutocomplete]="auto" [formControl]="customerFi ...

Unable to make changes while state transition is in progress

I want to update the state value based on the result of a promise method. To have optimistic updates, I set the state value before an asynchronous operation. If the operation fails, the state is reset. componentWillMount(){ this.setState({notify: this ...

Guide to selecting the radio button in group2 by clicking on the radio button in group1

Here is my scenario: I have two sets of radio buttons as shown in the code snippet below: <form name="form" method="post" action=""> <label><input type="radio" name="RadioGroup1" value="radio" id="Radio1">Radio 1</label><br> ...

What is the most effective way to access content from a webpage that is rendered

Is there a reliable way to download from links on a JavaScript rendered webpage using Python as the preferred language? I have attempted to use the Selenium Python bindings on a headless server, but it has proven to be slow, error-prone, and unable to acc ...

Attempting to import a 3D model in the format of .glb

Encountered error in console: Uncaught TypeError: Failed to resolve module specifier "three". Relative references must start with either "/", "./", or "../". Attempting to load a 3D model, here are my index.html and 3dmodel.js files. Am I overlooking some ...

What's the reason for switching from a custom tab icon to a React icon?

I've encountered a strange issue while working on my app - the tab icon keeps changing from the one I've set to the ReactJS icon whenever I navigate between different routes. I have linked the icon correctly in my HTML file, so I'm not sure ...

What is the method for sending a status header without redirecting to a different page?

function verifyRegistration() { const xhr = new XMLHttpRequest(); xhr.open("GET", "http://ipaddressandport/users/register"); xhr.onload = () => { console.log("tes"); if (xhr.status === 400) { console.log ...

Struggling with passing a function along with parameters to a component in React has been a challenge for me

Currently utilizing React in conjunction with NextJS My goal is to send a function, along with its parameters, to my 'Alerts' component so that it can wait for user input before executing the function. For instance, prior to clearing a list, I ...