Examining a feature by solely utilizing stubs

I've been immersed in writing tests for the past few weeks. In my workplace, we utilize Mocha as our test runner and Chai for assertions, with Sinon for creating stubs. However, there's a recurring issue that's been bothering me. I've written tests for several functions where I stub out every dependency without considering the arguments passed to the function being tested. Let me illustrate with an example:

module.exports = {
  "someFunc": (arg1, arg2) => {
    return new Promise((resolve, reject) => {
      Promise.all(arg1).then(data => {
        let someArray = ourHelperLib.toArray(data);
        let someObj = ourHelperLib.toObject(arg2);
          if(someArray.length == 0){
            reject("error");
          }else{
            resolve({
              "array": someArray,
              "object": someObj
            });
          }
        }).catch(err => {
            reject(err);
        });
    });
  },
}
  1. When testing this function, I've created a scenario where I stub Promise.all() to throw an error.
  2. In another test, I stub Promise.all() to return false and ourHelperLib.toArray() to throw an error, checking how the function handles it.
  3. For a third test, I stub all three: Promise.all(), ourHelperLib.toArray(), and ourHelperLib.toObject() to return false values and assess the output for a resolved promise after completing the operations.

It's evident from the function definition that the arguments are directly passed to the stubbed dependencies, leading me to overlook these values completely, like this:

const stubOurHelperLibToThrowError = argFromCaller => {
    throw new Error("This is an error");
}

By neglecting the argument handling within my stub functions, I realize I'm not effectively testing the input data of the function. Instead, I focus solely on validating the logic structure of someFunc().

Is this approach considered best practice? Seeking clarity on this topic as I aim to establish comprehensive guidelines for unit testing at my current workplace.

Peace!

Answer №1

You have the option to hand off promises to your function without needing to simulate anything for most of the scenarios you are discussing.


I encountered a situation where I faked Promise.all() to throw an error

Instead of faking Promise.all, simply provide an array with a rejected Promise to your function:

someFunc([Promise.reject(new Error('fail'))], null)

This will make the Promise.all go into the catch block and reject with the error.


I simulated Promise.all() to return a false positive result and stimulate ourHelperLib.toArray() to throw an error, then test if the function handles it correctly or not

Once again, rather than simulating Promise.all, pass an array with a resolved Promise:

someFunc([Promise.resolve('a value')], null)

You can either stimulate ourHelperLib.toArray to produce an error or resolve your Promise array to something that triggers ourHelperLib.toArray to throw.


For my third test, I faked Promise.all(), ourHelperLib.toArray(), and ourHelperLib.toObject() to return false positive results and then checked the output for a resolved promise containing the result after operations.

Faking ourHelperLib.toArray and ourHelperLib.toObject is optional. Unless they are resource-intensive (e.g., making network requests), it's usually better to call them normally.

You can feed the data intended for

ourHelperLib.toArray</code in the array of resolved <code>Promise
s and simply provide the value for ourHelperLib.toObject as the second argument:

someFunc([
  Promise.resolve('value 1 for ourHelperLib.toArray'),
  Promise.resolve('value 2 for ourHelperLib.toArray')
], 'value for ourHelperLib.toObject')

...and confirm that the resulting Promise resolves to the expected value.


In general, it is recommended to follow black box testing principles.

This function seems to have no side effects and merely returns a Promise that resolves to a result based on the input parameters.

Unless the function relies on resource-heavy dependencies, it is advisable to test a function like this by providing inputs and checking the outcome.

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

How to achieve the wrapping functionality in ReactJS that is similar to

Is there a ReactJS equivalent to jQuery's wrap method? I want to wrap menuContents with the following element: <ul className="nav nav-pills nav-stacked"></ul> The contents of menuContents are generated like this: let menuContents = thi ...

Accessing the react-bootstrap tab using the history feature in React

Currently, I am in the process of developing a multi-form page that includes login, registration, and lost password functionalities displayed using tabs with react-bootstrap. The technologies I am using for this project are react 17.0.2, react-bootstrap 2 ...

Tips on expanding the dimensions and incorporating more members in a radar graph's Chartjs tag

I need to make some adjustments to the font size and color in a radar chart. Specifically, I want to change the name on the side of each data point. I have already tried adjusting the legend labels using the following code: options={{ ...

I am attempting to transfer information from one page to another in Next.js, but unfortunately, I am experiencing difficulties in

Encountering the following error message: "Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead." Any assistance will be greatly appreciated. export default func ...

How can I differentiate between an unreachable server and a user navigating away in a $.ajax callback function?

Situation: You have a situation where several $.ajax requests to the server are still in progress. All of them end with xhr.status === 0 and xhr.readyState === 0. Possible reasons for this issue: The server might be down (EDIT: meaning it is unreachabl ...

Display a div element for a specified amount of time every certain number of minutes

I am currently utilizing AngularJS, so whether the solution involves AngularJS or pure JS does not make a difference. In the case of using AngularJS, I have a parameter named isShowDiv which will determine the switching between two divs based on the follow ...

Exploring Unanchored Substring Queries in Loopback API

Searching for a solution to implement a basic substring query using the loopback API for a typeahead field. Despite my efforts, I have not been able to find a clear answer. I simply want to input a substring and retrieve all brands that contain that subst ...

Changed over to a promise-oriented database, causing my login feature to malfunction completely

Although I can successfully register, when I am redirected to my game route, all I see is a blank Error page with [object Object] on the screen. This message also appears in my console periodically. Initially, I suspected an issue related to socket.io, bu ...

Getting a vnode from a DOM element in Vue 3.0: A Step-by-Step Guide

My question pertains to obtaining a vnode through accessing the DOM using document.getElementById(id). How can I accomplish this? ...

Having trouble displaying Vue Toast messages?

I have been trying to incorporate a toast message into my code, but unfortunately, the message does not appear and there are no errors in the console. The code functions properly and the invitation is sent successfully. I have used similar code in other fi ...

AngularJS Data Binding Issue - Watch cycle fails to trigger

View the JSFiddle example here: https://jsfiddle.net/pmgq00fm/1/ I am trying to achieve real-time updating of my NVD3 chart by utilizing the setInterval() function on line 39, which updates the data bound to the directive. Here is a brief overview of the ...

Exploring the Interplay of Classic ASP and AJAX Variables References

When the page loads, I check for an empty session variable. If it is empty, I trigger an AJAX function to include a hidden login form with ASP script that becomes visible through JavaScript. This part of the process works smoothly. Upon submitting the for ...

How can components be utilized with v-edit-dialog?

Hey there, I've been exploring the v-edit-dialog component offered by Vuetify and had a query about its implementation. Currently, I'm structuring my v-data-table in a way where I'm importing a component with props into a template slot. The ...

Reorganizing divisions with Dojo

I came across a thread on StackOverflow that was similar to what I'm looking for. jQuery removing an element and renumbering remaining elements However, since we don't use jQuery but instead rely on Dojo, I'm unsure how to achieve the same ...

Enhance the code for updating the content within a div element

I recently discovered that utilizing jQuery allows for updating the content within a div. My goal now is to enhance this script so the content remains consistent, even while loading or not. Take a look at my current code: function changeContent () { va ...

Clicking on the user will reveal a modal containing all of the user's detailed information

**I am trying to pass the correct user data to the modal ViewUser component, but it keeps displaying the same user regardless of which user I click on. How can I specify the specific user whose data should be shown? I am sending the user information as a ...

The Script Component is not functioning properly in next.js

While using Tiny Editor, I encountered an issue with defining a key for the editor. According to the documentation, I need to access this key through the tag <script src='address'. This method seems to work fine initially. However, when combin ...

An error occurred while attempting to reset your password on Parse.com (JS SDK) due to an

I am having trouble resetting my password in my angularjs App. I am utilizing Parse (js SDK) as the backend. Even though I am following the documentation and using Parse.User.requestPasswordReset, I keep encountering error 125 which states invalid email ad ...

Axios delivers the index.html data to the front end of a React.js application

I’m currently in the process of developing a web application using React.js for the front-end and Flask for the back-end. I attempted to establish a connection between the two by defining a proxy server in React and enabling CORS in Flask. Everything was ...

Processing a JSON array of objects in AngularJS

When using Angular's fromJson function to parse a JSON string, I encountered an issue. If the JSON is a simple array like "[1, 2]", the code works fine. However, I need to work with an array of dictionaries instead. var str = "[{'title' ...