Can Jest be used to mock an internal class object within a method?

I am facing an issue with a class dedicated to handling a data type known as ProductMetadata. This class interacts with a lookup service that selects a specific handler based on the type of ProductMetadata received from a LookupService object. I have been able to mock all objects except for the DataHandler instance created within the process method.

Below is the code snippet:

import { ProductMetadata } from './product/metadata'
import { LookupService } from './service/lookup'
import { DataHandler } from './product-handlers/data-handler'


class ProductDataHandler {

  async process(data: ProductMetadata, service: LookupService): Promise<void> {
    if (data.value) {
      try {
        const processor = await service.getProductHandlerForType(data.type)
        const dataHandler = new DataHandler(processor)
        await dataHandler.insert(data)
      } catch (error) {
        console.error('Failed to persist data ', error)
      }
    }
  }
}

export { ProductDataHandler }

The challenging part for me is mocking this line of code: Unfortunately, this instance is internally created and I'm unsure if Jest provides a way to effectively mock it. Everything else in the setup works fine apart from the fact that dataHandler remains undefined.

const dataHandler = new DataHandler(processor)

How can I successfully mock the DataHandler class?

I attempted the following approach but encountered a

TypeError: DataHander is not a constructor
:

jest.mock('./product-handlers/data-handler', () => (
  {
    __esModule: true,
    insert: jest.fn()
  }
))

``ts

    import {ProductProcessor } from './processor/product-processor'
   
    class DataHandler {
       private productProcessor:ProductProcessor
       constructor(productProcessor: ProductProcessor){
        this.productProcessor = productProcessor
       }
       ...
    }

Answer №1

Ensure proper mocking of the ./product-handlers/data-handler module and DataHandler class by using jest.mock() to create an auto-mocked version.

"jest": "^26.6.3"

product-data-handler.js:

import { DataHandler } from './product-handlers/data-handler'

class ProductDatatHandler {

  async process(data, service) {
    if (data.value) {
      try {
        const processor = await service.getProductHandlerForType(data.type)
        const dataHandler = new DataHandler(processor)
        await dataHandler.insert(data)
      } catch (error) {
        console.error('Failed to persist data ', error)
      }
    }
  }
}

export { ProductDatatHandler }

product-handlers/data-handler.js

class DataHandler {
  insert(data) {
    return Promise.resolve('ok')
  }
}

export { DataHandler };

product-data-handler.test.js:

import { DataHandler } from './product-handlers/data-handler'
import { ProductDatatHandler } from './product-data-handler'

jest.mock('./product-handlers/data-handler');

describe('Custom Test', () => {
  test('Validation', async () => {
    const dataHandlerInstanceMock = {
      insert: jest.fn()
    }
    DataHandler.mockImplementation(() => dataHandlerInstanceMock);

    const lookUpServiceMock = {
      getProductHandlerForType: jest.fn()
    }
    const productDataHandler = new ProductDatatHandler();
    await productDataHandler.process({ type: 'a', value: 'b' }, lookUpServiceMock);
    expect(lookUpServiceMock.getProductHandlerForType).toHaveBeenCalledWith('a');
    expect(dataHandlerInstanceMock.insert).toHaveBeenCalledWith({ type: 'a', value: 'b' });
  });
});

Outcome of the test:

 PASS  stackoverflow/76429246/product-data-handler.test.js (18.035 s)
  Custom Test
    ✓ Validation passed (6 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        18.944 s, estimated 25 s

UPDATE:

To handle TypeScript types effectively, utilize jest.mocked(source, options?)

const DataHandlerMock = jest.mocked(DataHandler);
// ...

DataHandlerMock.mockImplementation(() => dataHandlerInstanceMock);

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

When using the Composition API in Vue 3, the "Exclude" TypeScript utility type may result in a prop validation error

Currently, I am utilizing Vue 3 alongside the Composition API and TypeScript, all updated to their latest stable versions. If we take a look at the types below: export interface Person { name: string; } export type Status = Person | 'UNLOADED&ap ...

Tips for including a decimal point in an angular reactive form control when the initial value is 1 or higher

I am trying to input a decimal number with 1 and one zero like 1.0 <input type="number" formControlName="global_velocity_weight" /> this.form = this.fb.group({ global_velocity_weight: new FormControl(1.0, { validators: [Valida ...

Guide on transforming a tuple of random types into a nested type structure with the help of recursive conditional types

When I responded to the query on whether Typescript Interfaces can express co-occurrence constraints for properties, I shared the following code snippet: type None<T> = {[K in keyof T]?: never} type EitherOrBoth<T1, T2> = T1 & None<T2&g ...

Asynchronous handling of lifecycle hooks in TypeScript for Angular and Ionic applications

I'm intrigued by the idea of integrating TypeScript's async/await feature with lifecycle hooks. While this feature is undeniably convenient, I find myself wondering if it's considered acceptable to make lifecycle hooks asynchronous. After ...

Ways to avoid route change triggered by an asynchronous function

Within my Next.js application, I have a function for uploading files that includes the then and catch functions. export const uploadDocument = async (url: UploadURLs, file: File) => { const formData = new FormData(); formData.append("file" ...

Unable to get the Angular Formly select option to bind

I'm currently working on binding formly select type options with the following code: fieldGroup: [ { key: 'TimeOffTypeID', type: 'select', className: 'flex-40 padding-10', templateOptions ...

IntelliJ is indicating a typescript error related to react-bootstrap-table-next

Working with react-bootstrap-table-next (also known as react-bootstrap-table2) has been causing a Typescript error in my IntelliJ environment, specifically on the validator field within my column definition. Despite trying various solutions, such as adding ...

Is there a way to implement jquery (or other external libraries) within Typescript?

Currently, I am diving into Typescript to enhance my skills and knowledge. For a project that is being served with Flask and edited in VSCode, I am looking to convert the existing JavaScript code to Typescript. The main reason for this switch is to leverag ...

Exploring the power of Typescript functions within a traditional VueJS project

TL;DR: How can I import and use a typescript module into my plain js Vue-Components? I have a Vue 2 (not yet 3) project. In this specific project, I have made the decision to refactor some of the code logic into ES modules for improved testability and reu ...

Top-notch approach for consolidating Typescript Declaration files into a single comprehensive file

Is there any efficient way to package declaration files together without using the module declaration approach? declare module "path/to/file" { ... } declare module "path/to/file/sub/file" { ... } and so on. I have encountere ...

How can we import the entire Jasmine library using CucumberJS?

I am currently trying to implement unit testing using Jasmine and CucumberJS in my Angular v9 application. I have followed the tutorial provided by cucumber.io to set up cucumber as the default runner. However, I am facing difficulties in using Jasmine met ...

Furnish an item for a particular service

I am currently attempting to utilize a service created by another individual (github). This particular service requires a configuration to be passed to it. As stated in the repository: To configure Neo4jSettings in your bootstrap: provide('Neo4jSet ...

Configuring ESLint and Prettier with the Airbnb style guide for TypeScript in a React Native (Expo) project

I have been struggling with setting up linting and formatting for my React Native project for a while now. Despite following various tutorials, I still encounter setup issues. My project consists of a Django backend and a React Native frontend. I began im ...

What is the best way to execute 2 statements concurrently in Angular 7?

My goal is to add a key rating inside the listing object. However, I am facing an issue where the rating key is not displaying on the console. I suspect that it might be due to the asynchronous nature of the call. Can someone help me identify what mistak ...

What sets apart the utilization of add versus finalize in rxjs?

It appears that both of these code snippets achieve the same outcome: Add this.test$.pipe(take(1)).subscribe().add(() => console.log('added')); Finalize this.test$.pipe(take(1), finalize(() => console.log('finalized'))).sub ...

Is there a way to combine compiling TypeScript and running the resulting .js file into one build command in Sublime Text 3?

I have successfully installed the TypeScript plugin on Sublime Text 3. Once installed, a build system is added to the menu for easy access. https://i.stack.imgur.com/m21bT.png You can simply press "Command + B" to build a .ts file. My goal is to compile ...

Filtering through an array object with PrimeNG

Is it feasible to utilize an array of objects for filtering data in a table? I'm currently using Angular 6 and PrimeNG 7. This is how my p-table appears: <p-table #table class="ui-table ui-table-responsive" [value]="arrays" [columns]="cols" > ...

Resolving typescript error in my custom hook

Implementing this ResizeObserver hook in my project using typescript const useResizeObserver = () => { const [entry, setEntry] = useState<ResizeObserverEntry>(); const [node, setNode] = useState<Element | null>(null); const observer = ...

Determine the general type of a different type

Imagine I have the following scenario : const sub = new Subject<{ id: string; value: string; }>(); Is there a method to obtain the generic type assigned to the Subject object? const item: ??? = { id: '1', value: 'value' }; Alth ...

calculate the difference between two dates and then add this difference to a new date

Utilizing TypeScript for date calculations. Example: Initial Date 1: "10/06/2021 10:10:05" Initial Date 2: "08/06/2021 11:10:05" Calculate the difference between the two dates, including date/month/year/hour/min/sec/milliseconds. Ensure compatibility wi ...