Ways to simulate a constant that acts as a dependency for the service being examined?

I'm currently experimenting with a file named connect-key.js. It relies on a dependency called keyvault-emulator.

Content of File #1:

// connect-key.js file
const { save, retrieve, init  } = require('./keyvault-emulator')
....
....
....
// SOME TESTS

Content of File #2:

// The keyvault-emulator.js file
const { storageConnectionString } = require('../config')

Now, I'm wondering how to simulate the value of storageConnectionString in my test file connect-key.spec.js?

My idea is something like this:

// The connect-key.spec.js file
const keyvault_emulator = require('../keyvault-emulator');
const spy = jest.spyOn(keyvault_emulator, 'storageConnectionString');
spy.mockReturnValue('');

This is the content of the config file:

// config.js file
module.exports = {
  storageConnectionString: process.env.STORAGE_CONNECTION_STRING || process.env.Storage,
  keyVaultName: process.env.KEY_VAULT
}

Is this the correct approach? What is the optimal way to achieve this?

Answer №1

Exploring Mocking Internal Dependencies

Prior to mocking internal dependencies, it is essential to define the purpose behind it; especially considering Jest's limitations in this regard. This article aims to present some potential scenarios and examples that can shed light on this topic.

Let's delve into a hypothetical scenario where there exists a function named testFunction() that returns the previously mentioned storageConnectionString.

1. Targeting Specific Functions within Test Scope

// key.spec.js
const keyvault_emulator = require('../keyvault-emulator');
js.mock('../keyvault-emulator', () => ({
  // everything remains untouched except for testFunction
  ...jest.requireActual('../keyvault-emulator'),
  // mock implementation of testFunction
  testFunction: jest.fn(() => 'mocked')
})

// ✅ Success
expect(keyvault_emulator.testFunction()).toEqual('mocked')
// ❌ Failure!
expect(keyvault_emulator.otherFunctionUsingStorageConnectionString())
  .toEqual('mocked')

2. Replacing all Module Content

In Jest, the capability lies in substituting an entire function or module rather than re-evaluating code snippets. In cases like this, decoupling dependencies such as config.js and keyvault-emulator.js from the source proves to be beneficial for testing purposes.

2-1. Implementing Hard Decoupling directly in the Source

// keyvault-emulator.js
class KeyValueEmulator {
  constructor(externalConfig) {
    this.externalConfig = externalConfig;
  }
  testFunction() {
    // process involving this.externalConfig
    return this.externalConfig;
  }
}

// key.spec.js
const mockedExternalConfig = { storageConnectionConfig: 'mocked' }
const keyValueEmulator = new KeyValueEmulator(mockedExternalConfig);
// ✅ Successful execution
expect(keyValueEmulator.testFunction()).toEqual('mocked')

2-2. Employing Soft Decoupling with Jest's Internal Module Mocking

Refer to working codes on GitHub

// key.spec.js
import config from "./config";
jest.mock("./config", () => ({ default: { storageConnectionString: "mocked" } }));
import { storageConnectionString, testFunction } from "./index";

describe("config mocking", () => {
  it("value is mocked", () => {
    // ✅ Works as expected
    expect(config.storageConnectionString).toEqual("mocked");
    expect(testFunction()).toEqual("mocked");
  });
});

3. Mocking a Single Variable

As elaborated in case 2, mocking a single variable poses challenges with Jest's current functionalities. Hence, precise clarification and adjustments are needed when attempting such operations.

// key.spec.js
const keyvault_emulator = require('../keyvault-emulator');
const spy = jest.spyOn(keyvault_emulator, 'storageConnectionString');
spy.mockReturnValue('mocked');
// ✅ Potential success...however
expect(keyvault_emulator.storageConnectionString).toEqual('mocked')
// ❌ Unsuccessful outcome
expect(keyvault_emulator.testFunction()).toEqual('mocked')

Determining the Optimal Approach

Is this method considered effective? What strategy works best for achieving desired outcomes?

Similar to numerous developer dilemmas, the most suitable approach varies depending on the specific context. While opting for hard decoupling, as highlighted in 2-1, serves well in many scenarios, it may not always be the perfect solution. Select the methodology that aligns seamlessly with your unique circumstances.

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

Unusual escape_javascript quirk witnessed in Ruby on Rails

Once the ajax method is called, the escape_javascript function starts outputting </div> </div> </div> for each rendered item. Despite thoroughly checking all closing tags multiple times, I can't seem to identify any errors. Could thi ...

The connection to MongoDB is failing due to an incorrect URI

I tried setting up mongoDB on my node server and referred to the official MongoDB documentation. Here are the details of my setup: MongoDB version: 4.4.3 Node.js version: v15.7.0 I copied the starter code from MongoDB and here is what I used: const { Mon ...

Determine the value of a field by utilizing the values of two other fields through an onChange event

Setting the Stage: Imagine a scenario with 3 key fields: quantity, singlePrice, and totalPrice. I want these fields to be part of my form, with totalPrice being dynamically recalculated whenever quantity or singlePrice changes. My Approach: I created ...

What is the best way to address Peer dependency alerts within npm?

Here is a sample package.json that I am using for my application: dependencies : { P1 : “^1.0.0” // with peer dependency of p3 v1 P2 : “^1.0.0” // with peer dependency of p3 v2 } P1 and P2 both have peer dependencies on ...

Tips for optimizing the sequencing of 3 ajax requests, with the output of one request serving as input for the subsequent two requests

I'm currently working on a solution for chaining 3 ajax calls where the result of one call feeds into the next two. Here's the scenario: // Invoke the ajax calls firstAjax('mypage.gng','john-doe').then(secondAjax, thirdAjax) ...

Click on a specific image in a table using Cypress with a specific source URL

I need help with a query for Cypress regarding a table of items, each item having active (blue) and not active (black) images. How can I set up this query? Below is an image of the table list: https://i.stack.imgur.com/74qzb.png And here is the HTML code ...

Prevent clicking on the <div> element for a duration of 200 milliseconds

I need to make this box move up and down, but prevent users from clicking it rapidly multiple times to watch it go up and down too quickly. How can I add a 200 millisecond delay on the click or disable clicking for that duration? View the jsfiddle example ...

What is the best way to retrieve all information from GitLab API using Axios?

I am looking to retrieve data from all the projects stored on my GitLab server. As I understand, GitLab usually displays a default of 20 projects per page, so I need to adjust the setting to show more projects at once: https://gitlab-repo.com/api/v4/proje ...

Loading external libraries in Angular2: A step-by-step guide

I'm currently working on incorporating a Datepicker in Angular 2, but I'm facing an issue where the library is not loading. This is causing a template parsing error stating 'material-datepicker' is not a recognized element: My System.c ...

Issues with Contenteditable functionality in JavaScript

My goal is to make a row editable when a button is clicked. $(":button").click(function(){ var tdvar=$(this).parent('tr').find('td'); $.each(tdvar,function(){ $(this).prop('contenteditable',true); }); }); <s ...

a solution to the focus/blur issue in Firefox's browser bug

I have created the following script to validate if a value entered in an input field already exists in the database. $('#nome_field').blur(function(){ var field_name=$('#nome_field').val(); $.ajax({ url: " ...

Incorporating a computed variable in a v-select within a VueJs endless loop

I've been attempting to utilize a computed value in a v-select component from Vuetify, but every time I select an item, it triggers an endless loop. To demonstrate the issue, I have recreated my code in this CodePen. Please be cautious as it may caus ...

Which Web Browsers Support Canvas and HTML5?

Currently working on a project that involves using the HTML5 Canvas element. I am curious about which major browsers (including specific versions) actually support the Canvas tag. I am not interested in hearing about IE. In this tutorial Drawing shapes - M ...

Error: Attempting to access the property 'push' of an undefined variable has resulted in an unhandled TypeError

if (Math.random() <= .1) { let orgAdmin = User.find({email: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1234454665324721380c0d">[email protected]</a>'}); or ...

Preventing the callback nightmare in nodeJs / Sharing variables with nested functions

Let's simplify this scenario: const generateUrl = (req, res) => { const id = req.query.someParameter; const query = MyMongooseModel.findOne({'id': id}); query.exec((err, mongooseModel) => { if(err) { / ...

What is the best way to add a new item to an object using its index value?

Can the Locations object have a new property added to it? The property to be added is: 2:{ name: "Japan", lat: 36, lng: 138, description: 'default', color: 'default', url: 'default' } The current Location ...

Unable to retrieve $scope.property using direct access, however it is visible when printed to the console using console.log($

I have successfully populated $scope with data using a get call: httpGetAsync("myUrlWasHere", getBlogPosts, $scope); The console outputs the data when I print console.log($scope): However, when I try to access it using console.log($scope.blogPosts), it ...

Issue with implementing MUI Style Tag in conjunction with styled-components and Typescript

I have created a custom SelectType component using Styled Components, which looks like this: import Select from '@mui/material/Select'; export const SelectType = styled(Select)` width:100%; border:2px solid #eaeaef; border-radius:8px ...

To ascertain whether the mouse is still lingering over the menu

I have a condensed menu construction that unfortunately cannot be changed in HTML, only CSS and JS modifications are possible! $('span').on("hover", handleHover('span')); function handleHover(e) { $(e).on({ mouse ...

What sets apart the JavaScript console from simply right-clicking the browser and opting for the inspect option?

As I work on developing an angular application, one of my tasks involves viewing the scope in the console. To do this, I usually enter the code angular.element($0).scope(). This method works perfectly fine when I access the console by right-clicking on th ...