An effective way to mimic an un-exported (private) function within a user module using Jest

In my code, I have a function that handles API requests called "private" and several other functions that initiate specific requests with configuration objects. For example, in the requestUploadStatementFile file.

I want to test these public functions, but I'm unsure how to mock the private function using Jest, specifically the requestWithAutoTokenRenew function.

/**
 * An API wrapper that automatically renews JWT tokens once they expire
 *
 * @param {Object} requestConfig Request configuration object
 * @returns {Promise}
 */
const requestWithAutoTokenRenew = async requestConfig => {
  const session = await doGetSession();
  const sessionToken = session.idToken.jwtToken;
  const { url, method, params, payload } = requestConfig;
  const requestObj = {
    url,
    method,
    headers: {
      Accept: "application/json",
      Authorization: sessionToken,
      "Content-Type": "application/json"
    },
    data: payload,
    ...params
  };

  return axios.request(requestObj).then(response => response.data);
};

/**
 * Upload bank or credit card statement for parsing
 *
 * @param {Object} file Statement PDF file needs to be parsed
 */
export const requestUploadStatementFile = file => {
  const requestConfig = {
    url: URL_UPLOAD,
    method: "POST",
    payload: file
  };

  return requestWithAutoTokenRenew(requestConfig);
};

Answer №1

In my approach, I decided not to mock the entire "private" function but instead focus on mocking specific parts of the functionality within that function. Specifically, I targeted the challenging aspects such as retrieving the token from the remote service using doGetSession and making calls to an external API with Axios lib's request method.

// Mocks
import { request } from "axios";
import { doGetSession } from "../utilities/auth/auth";

// Targets
import { requestUploadStatementFile } from "./api";

jest.mock("../utilities/auth/auth");
jest.mock("axios");

describe("requestUploadStatementFile", () => {
  it("should trigger the request with the correct configuration object", done => {
    doGetSession.mockImplementationOnce(() => {
      return Promise.resolve({ idToken: { jtwToken: "SAMPLE-TOKEN" } });
    });

    request.mockImplementationOnce(() => {
      return Promise.resolve({ data: [] });
    });

    requestUploadStatementFile({}).then(transactions => {
      const transactionsExpected = [];
      const requestExpectedArgs = {
        data: {},
        headers: { Accept: "application/json", Authorization: undefined, "Content-Type": "application/json" },
        method: "POST",
        url: "https://*.*.amazonaws.com/api/upload"
      };

      expect(transactions).toEqual(transactionsExpected);
      expect(request).toHaveBeenCalledTimes(1);
      expect(request).toHaveBeenCalledWith(requestExpectedArgs);

      done();
    });
  });
});

A special thanks goes out to @felixmosh for the insightful comment.

It is generally frowned upon to mock private functions. It's best practice to focus on mocking the outer layers of your application, typically the public APIs.

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

The React functional component fails to update when triggered by a parent component's setState method

My React component is utilizing apollo to fetch data through graphql class PopUpForm extends React.Component { constructor () { super() this.state = { shoptitle: "UpdateMe", popupbodyDesc: "UpdateMe" } } re ...

What is the reason my hyperlinks are not clickable?

I'm working on a project that checks if a Twitchtv user is live streaming and provides a link to their page. The streaming part is working correctly, but I'm having trouble making the username clickable. Even though the URL is valid and the page ...

What could be the reason for the lack of read or write functionality in $cookies for angular version 1.6.6?

I am relatively new to working with Angular. I have started a test project and my goal is to store user authentication in cookies. Despite my best efforts, I keep encountering an undefined error when attempting to retrieve the stored value. I am not sure w ...

"Discover the magic of jQuery: Unveiling the hidden div with one simple CSS visibility change

I want to implement a functionality on my screen where the Previous button is hidden initially and only becomes visible when the user clicks the Next button. I have set the CSS property for the Previous button to hide it by default, but despite using an if ...

Revolutionize iPad user experience with seamless HTML5 video integration

What is the most effective method for dynamically embedding HTML5 video to ensure compatibility with the iPad? (using pure Javascript) I'm having difficulty getting this approach to work: <div id="placeholder"><script type="text/javascript" ...

Trigger Knockout bindings once the ajax request has been completed

In the view, I have the following script: <script> MyObj.initModel(getUrl); $(document).ready(function () { ko.applyBindings(MyObj.viewModel, document.getElementById("someId")); ...

The selected data is not being displayed

My input field is not displaying anything. Below is the script function in my view: <script> var Features = []; function LoadFeatures(element) { if(Features.length === 0) { $.ajax({ url:'@Url.Action("GetFeatures"," ...

VueJS Components experiencing issues with displaying images

Recently, I delved into learning VueJS and successfully created a basic restaurant menu page all within a single file. Excited by my progress, I decided to refactor the project using vue-cli, but hit a snag with the images not displaying properly. The cur ...

The webpage loaded through ajax is not rendering correctly

One of the challenges I'm facing is getting a JavaScript script to load an HTML page into a specific div element on another HTML page: The page that's being loaded, startScreen.html, looks like this: <!DOCTYPE html> <html lang="en ...

The partial template is not functioning as anticipated

Introducing an interface designed to accept two templates, with the resulting function being a partial of one of them (similar to React-Redux): export type IState<TState, TOwnProps> = { connect: (mapStateToProps: MapStateToProps<TState, Parti ...

Can one jQuery script be used for multiple ajax 'like' buttons?

Hey there! I'm working on an Ajax 'like' button that utilizes jQuery. This button will be utilized multiple times on a single page. I'm looking for a way to streamline the process and avoid including the jQuery script multiple times. Is ...

Ensure Safari sends the Origin header in jQuery GET requests

When I send a request from https://app.example.com, the following code is executed: $.get('https://api.example.com', { foo: 'bar' }) .success(getSuccess) .error(getError); This script runs smoothly on Chrome and Firefox, however, ...

Preserving variable scope in JavaScript even after defining a function

I am facing an issue with my JavaScript code that involves invoking a function within a function: var obj = { // returns the function with prevent default prepended. run: function(functor, context){ return function(e){ e.preventDefault(); ...

Angular image source load test encountered an error

<div class="col-xs-4 col-sm-4 col-md-4"> {{jsonData[current].profilepic}} <div ng-if=IsValidImageUrl(jsonData[current].profilepic)> <img id="pic" ng-src="{{jsonData[current].profilepic}}" alt=""/> </div> < ...

Pagination Component for React Material-UI Table

I am interested in learning about Table Pagination in React UI Material. Currently, my goal is to retrieve and display data from an API in a Material UI Table. While I have successfully implemented some data from the API into the Material UI Table, I am ...

The term 'EmployeeContext' is being utilized as a namespace in this scenario, although it actually pertains to a type.ts(2702)

<EmployeeContext.Provider> value={addEmployee, DefaultData, sortedEmployees, deleteEmployee, updateEmployee} {props.children}; </EmployeeContext.Provider> I am currently facing an issue mentioned in the title. Could anyone lend a hand? ...

.load() not triggering for images that are not in the cache - jquery

In Safari and Opera, the callback function of .load doesn't wait for uncached images to be fully loaded. With cached images, it works perfectly fine. The code may seem a little complicated, but I'll do my best to simplify it... So, here is my J ...

"Troubleshooting the issue of Delete Requests failing to persist in Node.js

Whenever I send a delete request to my node.js server, it can only delete one item from my JSON file until the server restarts. If I attempt to make a second delete request, it successfully deletes the item but also reverts the deletion of the last item. ...

"Discover the steps to seamlessly integrating Snappuzzle with jQuery on your

I am a beginner when it comes to javascript and jquery, and I recently came across the snappuzzle plugin which caught my interest. After visiting snappuzzle plugin, I decided to download and link jQuery, jQuery UI, and the snappuzle.js in my HTML file. I a ...

Determine if a JSON object is void

Using jQuery, I am checking whether the object returned from an AJAX call is empty or not. In the first example, the AJAX call is successful and returns some data. console.log("obj before JSON parse:", response); var test = $.isEmptyObject(response); con ...