Mocking a common function in a shared service using Typescript and Jest

I have a service that is utilized with NestJS, although the issue at hand is not specific to NestJS. Nonetheless, testing in NestJS is involved, and I use it to create the service for testing purposes. This service is responsible for making multiple calls and returning all the gathered data.

In my particular case, there is a service (myservice) which retrieves results (an object of data) by combining information from various sources. These external sources are all Promise-based using async/await functionality and are used to gather different data points, which are then combined within the myservice (a base service extended by others) to handle error management, processing of promise results, etc.

The challenge I am facing revolves around mocking the external calls for testing. Specifically, the difficulty lies in mocking the function responsible for fetching data from NodeJS. While I can test this function independently without any problems, attempting to mock out this function call within my service leads to complications.

Here's a brief excerpt of the functions under consideration as part of: myservice.ts

public async GetData(DataType: number = DATA_TYPES.TEXT): Promise<ResultFile> {
    const ResultData: ResultFile = new ResultFile(DataType);

    return new Promise<ResultFile>((resolve, reject) => {
        const Data$: Promise<string> = this.GetResultFileContents();
        const StatusCode$: Promise<number> = this.GetResultFileStatusCode();

        Promise.all([Data$, StatusCode$])
            .then((Results) => {
                ResultData.Contents = Results[0]; // Promise returns a string
                ResultData.StatusCode = Results[1]; // Promise returns a number

                resolve(ResultData);
            })
            .catch((Exception: Error) => {
                ResultData.StatusCode = HttpStatus.NOT_FOUND;
                ResultData.Contents = Exception.message;
                reject(ResultData);
            });
    });
}

The above method is the core operation dedicated to retrieving various data. It triggers more promises than the mentioned 2, but these two serve to exemplify the issue.

public async GetResultFileContents(): Promise<string> {
    try {
        const Results$: string = await ReadFileContents(this.Directory_, this.BaseName_);
        return Results$;
    } catch (Exception) {
        throw new HttpException(`File not found: ${this.BaseName_} in ${this.Directory_}`, HttpStatus.NOT_FOUND);
    }
}

public async GetResultFileStatusCode(): Promise<number> {
    let StatusCode: number = 0;
    try {
        const StatusCodeFile: string = `${this.BaseName_}.err`;
        const StatusCodeData$ = await ReadFileContents(this.Directory_, StatusCodeFile);
        StatusCode = GetIntegerFromText(StatusCodeData$);
    } catch (Exception) {
        StatusCode = HttpStatus.OK; // Return OK since a specific status was not set to be returned.
    }
    return new Promise<number>((resolve) => {
        resolve(StatusCode);
    });
}

The two methods being called to return promises both invoke an external function, ReadFileContents(). This is the function I aim to mock as it provides the data as a string or raises an exception encapsulating OS checks among other tasks related to the file(s) containing the data.

This function is commonly shared by several methods responsible for reading data from the file system. Similar issues exist with REST calls, though this example simplifies matters.

The issue surfaces in the test file. Though I can successfully test the service and its internal methods within myservice.ts, I'm unsure how to effectively mock the external call to ReadFileContents() to verify proper execution of my methods in myservice.ts.

I aim to examine varying return strings along with exception handling when files are missing, among other scenarios.

My test:

import { Test, TestingModule } from '@nestjs/testing';

import { MyService } from './my.service';

// TODO: Need to mock the internal calls to ReadFileContents() to throw exceptions
describe('MyService (mock)', () => {
    let service: MyService;

    afterEach(() => {});

    beforeEach(async () => {
        const module: TestingModule = await Test.createTestingModule({
            providers: [MyService],
        }).compile();

        service = module.get<MyService>(MyService);
    });

    it('should be defined', () => {
        expect(service).toBeDefined();
    });

    it('should handle exceptions when reading missing .hdr file', async () => {
        // const mockReadFileContents = jest.spyOn(service.???);
    });
});

In the last function, I'm uncertain about mocking ReadFileContents as it's merely a function within the service located in another source file.

I wish to avoid creating a public method in the service solely for calling this function to enable proper mocking, if possible.

Please disregard any missing functions or typos as this was a quick edit to showcase what I aim to achieve.

Answer №1

After persistent efforts, I was unable to make the mock function work as intended. However, I made adjustments by moving the functions I wanted to mock into another service called fsService and implemented this service in myService using Dependency Injection through the constructor.

constructor(
    private FileSystem_: fsService,
) {}

This setup allowed for easy mocking during testing by either mocking the service or providing a fake definition ( {provide: fsService, useClass: fsServiceMock }) etc.

import { Test, TestingModule } from '@nestjs/testing';
import { MyService } from './my.service';
import { FsService } from './fs.service';

describe('MyService (mock)', () => {
    let service: MyService;
    let FSService_: FsService;

    afterEach(() => {
        jest.resetAllMocks();
    });

    beforeEach(async () => {
        const module: TestingModule = await Test.createTestingModule({
            providers: [
                FSService,
                MyService,
            ],
        }).compile();
        FsService_ = module.get<FSService>(FSService);
        service = module.get<MyService>(MyService);
    });

    it('should be defined', () => {
        expect(service).toBeDefined();
    });

    it('should handle exceptions when reading missing .hdr file', async () => {
        FSService_.ReadFileContents = jest.fn().mockRejectedValue(new Error('ENOENT: File not Found'));
        ...
    });
});

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

Animating progress bars using React Native

I've been working on implementing a progress bar for my react-native project that can be used in multiple instances. Here's the code I currently have: The progress bar tsx: import React, { useEffect } from 'react' import { Animated, St ...

Unused code splitting chunk in React production build would improve performance and efficiency of

When running the command npm run build, a build directory is generated with js chunks. I have observed an unfamiliar file named [number].[hash].chunk.js that does not appear in the list of entrypoints in the asset-manifest.json file. Instead, this mysteri ...

Using Angular to access HTML content through the .ts file

Is there a way to retrieve the value of the input field [newUser] when clicking on the button and executing the action [onAddUser()] in the .ts file? <input type="text" ng-model="newUser" style="text-align:center"/> <button (cl ...

The recognition of Angular ngrx union type actions is hindered by discrepancies in constructors

The actions classes and union type are displayed below. Unfortunately, these actions are not being recognized during the application's execution. export class Login implements Action { readonly type = LOGIN; constructor( public payload: { ...

class-validator: ensures the correct number of digits are present in numeric values

Seeking assistance on validating the number of digits for numeric values using class-validator. Specifically, I want my entity to only accept numbers with 6 digits for a certain property. For example: const user1 = new User(); user1.code = 123456 // should ...

What is the reasoning behind not allowing an empty object as the initial value for hooks?

I am encountering an issue with my setProd Hooks. export interface faceProduct { readonly title: string; readonly prodState: string; readonly shipping: string; readonly sold: string; readonly alt: string; readonly material: string; readonly ...

Using an Angular interface for an HTTP request: The statusText message reads as "Error: Unable to Determine."

I've been working on calling an API as an example in Angular using an interface. The API I'm trying to access is located at https://jsonplaceholder.typicode.com/posts. Unfortunately, I encountered the following error message: ERROR HttpErrorResp ...

Exploring the World of ESLint, Prettier, Typescript, and VScode Configuration

There is a common belief that Prettier is the preferred tool for formatting, while ESlint is recommended for highlighting linting errors, even though ESlint also has formatting capabilities. However, it should be noted that Prettier lacks certain advanced ...

Issue with displaying selected value and options in Mat-select when using formarray - Reactive forms in Angular

I've been working on the code below to create dropdowns, but I'm having trouble getting the selected value and options to show up in the dropdowns. Can you help me figure out what's wrong with the code? Component code testForm: FormGroup; ...

Using Angular 2's ngModel directive to bind a value passed in from an

Using [(ngModel)] in my child component with a string passed from the parent via @Input() is causing some issues. Although the string is successfully passed from the parent to the child, any changes made to it within the child component do not reflect bac ...

Form for creating and updating users with a variety of input options, powered by Angular 2+

As I work on creating a form, I encounter the need to distinguish between two scenarios. If the user selects 'create a user', the password inputs should be displayed. On the other hand, if the user chooses to edit a user, then the password inputs ...

Tips for conducting a worldwide search in Angular 2?

I'm currently navigating my way through angular2 development and I am aiming to conduct a comprehensive search within an array of JSON objects. To illustrate, consider this sample array: invoiceList = [ { invoiceNumber: 1234, invo ...

You have to include the necessary request body in the front-end request to address the

After successfully testing a POST request to add a new entity to the database from my project back-end using Postman, I encountered an error when trying to do the same from the front UI (I'm using Angular 4): Required request body is missing. Failed ...

Using object in TypeScript to reduce arrays

Is there a way to set the return value for my reducer in TypeScript? I am looking to achieve: Instead of using 'any', what should I assign as the type for acc? How can I define my return type so that the output will be {temp: 60, temp: 60}? retu ...

Encountered an issue while attempting to integrate Nebular into my Angular application

As a newcomer to Angular, I decided to try installing Nebular using the command ng add @nebular/theme. However, I encountered an error in the process. Upon entering the command into my terminal, the following error message appeared: ? Which Nebular theme ...

Issue encountered while attempting to launch Angular2 project

Having trouble running my Angular2 project, every time I try to use the ng serve command I get an error: Here is the link: https://i.sstatic.net/SYENX.png I've installed angular 2 using angular-cli with the following steps: 1. sudo npm install -g ...

Having trouble appending a new attribute to the Mongoose output

In my Nodejs server application, I am working with a userDetail document that contains all the relevant user information. Additionally, I have a login document that stores the time of the first login, which I need to incorporate into the userDetails result ...

Bidirectional data binding on the table

I am struggling to implement two-way data binding in a table between my .ts and my .html files. I have a structure that adds a new equipment to the array, and I want that new equipment to display on the table within the same screen. I believe it involves ...

Exploring ways to access elements within shadow-root (open) in Angular using SVG.js

I'm currently tackling a project involving Angular Elements. Within this specialized component, my goal is to incorporate SVG.js 3+. However, due to the necessity of utilizing ViewEncapsulation.ShadowDom in my component, I am encountering challenges w ...

Steps to easily set up a date-range-filter in Angular 6!

I have put together a small StackBlitz project to showcase my current situation. My aim is to log all objects that fall within a specified date range. However, when I attempt to filter my objects, I am faced with an empty array as the result. I would like ...