Simulate a complete class with its constructor, instance methods, and static functions

I have been searching for a comprehensive example that demonstrates how to properly mock all three elements (ClassA constructor, ClassA.func1 instance function, and ClassA.func2 static function) in my TypeScript project. In the code under test, I need to verify the mocked constructor, function, and static function using Jest for unit testing purposes.

While I typically use the mock factory approach for mocking Classes, I am facing difficulties in implementing it in this scenario. Below is an outline of the structure:

// ClassA.ts
export class ClassA {
  constructor() {
    // construction logic
  }

  public async func1(x: int): Promise<void> {
    // func1 logic
  }

  static func2(x: string | number): x is string {
    // func2 logic
    let conditionIsMet: boolean;

    // conditionIsMet logic

    if (conditionIsMet) {
      return true;
    }

    return false;
  }
}

// CodeUnderTest.ts
import { ClassA } from './ClassA';

export class ClassB {
  public async foo() {
    if (ClassA.func2('asdf')) {
      const a = new ClassA();
      await a.func1(45);
    }
  }
}

// UnitTest.ts
import { ClassB } from './ClassB';

// mock factory
const mockFunc1 = jest.fn();
const mockStaticFunc2 = jest.fn();
jest.mock('./ClassA', () => ({
  ClassA: jest.fn().mockImplementation(() => ({
    func1: mockFunc1,
    func2: mockStaticFunc2, 
  }),
});

beforeAll(() => {
  mockStaticFunc2.mockReturnValue(true);
});
describe('when ClassB.foo() is called', () => {
  describe('when ClassA.func2 is given a string parameter', () => {
    it('then ClassA.func2 is invoked with expected parameters, ClassA is constructed, ClassA.func1 is invoked with expected parameters', () => {
      // arrange
      const clsB = new ClassB();

      // execute
      clsB.foo();

      // expect
      expect(mockStaticFunc2).toHaveBeenCalledTimes(1);
      expect(mockStaticFunc2).toHaveBeenNthCalledWith(1, 'asdf');
      expect(ClassA).toHaveBeenCalledTimes(1);  
      expect(mockFunc1).toHaveBeenCalledTimes(1);
      expect(mockFunc1).toHaveBeenNthCalledWith(1, 45);
    });
  });
});

Could someone provide a detailed solution or example that outlines how to effectively mock all aspects (ClassA constructor, ClassA.func1 instance function, and ClassA.func2 static function) as described?

Answer №1

You may want to double-check your implementation of mocking the static method func2. Remember, static methods are accessed on the class itself. For more information, refer to this link: es6-class-mocks#automatic-mock

Here is an example that demonstrates how to mock static and instance methods:

ClassA.ts:

export class ClassA {
  constructor() {}
  public async func1(x: number): Promise<string> {
    return 'real value';
  }
  static func2(x: string | number) {
    return false;
  }
}

ClassB.ts:

import { ClassA } from './ClassA';

export class ClassB {
  public async foo() {
    if (ClassA.func2('asdf')) {
      const a = new ClassA();
      const r = await a.func1(45);
      console.log("🚀 ~ file: ClassB.ts:8 ~ ClassB ~ foo ~ r:", r)
    }
  }
}

ClassB.test.ts:

import { ClassB } from './ClassB';
import { ClassA } from './ClassA';

jest.mock('./ClassA');

describe('when ClassB.foo() is called', () => {
  describe('when ClassA.func2 is given a string parameter', () => {
    it('then ClassA.func2 is invoked with expected parameters, ClassA is constructed, ClassA.func1 is invoked with expected parameters', async () => {
      expect(jest.isMockFunction(ClassA)).toBe(true);
      expect(jest.isMockFunction(ClassA.func2)).toBe(true);
      expect(jest.isMockFunction(ClassA.prototype.func1)).toBe(true);
      const ClassAMocked = jest.mocked(ClassA);

      // arrange
      const clsB = new ClassB();
      jest.mocked(ClassA.func2).mockReturnValueOnce(true);
      jest.mocked(ClassA.prototype.func1).mockResolvedValueOnce('fake value');

      // execute
      await clsB.foo();

      // expect
      const classAInstance = ClassAMocked.mock.instances[0];
      expect(jest.isMockFunction(classAInstance.func1)).toBe(true);
      expect(ClassAMocked.func2).toHaveBeenNthCalledWith(1, 'asdf');
      expect(ClassAMocked).toHaveBeenCalledTimes(1);
      expect(classAInstance.func1).toHaveBeenNthCalledWith(1, 45);
    });
  });
});

Test result:

  console.log
    🚀 ~ file: ClassB.ts:8 ~ ClassB ~ foo ~ r: fake value

      at ClassB.log (stackoverflow/77559902/ClassB.ts:8:15)

 PASS  stackoverflow/77559902/ClassB.test.ts
  when ClassB.foo() is called
    when ClassA.func2 is given a string parameter
      ✓ then ClassA.func2 is invoked with expected parameters, ClassA is constructed, ClassA.func1 is invoked with expected parameters (11 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.508 s, estimated 1 s
Ran all test suites related to changed files.

package versions:

"jest": "^29.7.0"

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 can I retrieve a certain type of object property in TypeScript?

Imagine having a collection of flags stored in an object like the example below: type Flags = { flag1: string, flag2: string, flag3: boolean, flag4: number } // const myFlags: Flags = { // flag1: 'value 1', // flag2: 'value 1&ap ...

What is the reason behind tsc (Typescript Compiler) disregarding RxJS imports?

I have successfully set up my Angular2 project using JSPM and SystemJS. I am attempting to import RxJS and a few operators in my boot.ts file, but for some reason, my import is not being transpiled into the final boot.js output. // boot.ts import {Observa ...

Mocked observables are returned when testing an Angular service that includes parameters

I'm currently exploring various types of unit testing and find myself struggling with a test for a service once again. Here is the function in my service that I need to test: Just to clarify: this.setParams returns an object like {name: 'Test&ap ...

What is the method for retrieving the index of an enum member in Typescript, rather than the member name?

Here is an example of how to work with enums in TypeScript: export enum Category { Action = 1, Option = 2, RealEstateFund = 3, FuturesContract = 4, ETFs = 5, BDRs = 6 } The following function can be used to retrieve the enum indexe ...

Adding an arrow to a Material UI popover similar to a Tooltip

Can an Arrow be added to the Popover similar to the one in the ToolTip? https://i.stack.imgur.com/syWfg.png https://i.stack.imgur.com/4vBpC.png Is it possible to include an Arrow in the design of the Popover? ...

What is preventing Angular from letting me pass a parameter to a function in a provider's useFactory method?

app.module.ts bootstrap: [AppComponent], declarations: [AppComponent], imports: [ CoreModule, HelloFrameworkModule, ], providers: [{ provide: Logger, useFactory: loggerProviderFunc(1), }] ...

Having trouble getting access to FormArray content for validation due to receiving null or undefined errors

CSS Tricks <form [formGroup]="newMovieForm"> <ng-container formArrayName="actors"> <ng-container *ngFor="let actor of (actors['controls'] || []) ; let i = index"> <div [formGroupN ...

How does TypeScript provide me with insights even before compiling with tsc?

As I follow the instructions for incorporating TypeScript into my existing React Native project here, the final step instructs me to: Run yarn tsc to type-check your new TypeScript files. However, when I check VSCode, I am already receiving feedback from ...

There seems to be an issue with the response type in the node.js environment

I am currently working on node.js and typescript, but I am encountering a minor issue. Below is the routeController I have created: public allUsers = (req: Request, res: Response) => { res.status(500).json({ status: "ERROR", ...

Assign a value using the select component from Material UI

I just finished creating a dropdown menu const [selectedValue, setSelectedValue] = useState(''); const handleSelectionChange = (e: any) => { //setSelectedValue(e) console.log('value', selectedValue) } .... <Fo ...

What is the best way to create an array of strings that can include multiple elements from a set of strings?

Exploring different roles for authorization: ['admin', 'manager', 'user'] Is there a way to create a specific type, named Roles, which is essentially a string array ( string[] ) that is limited to only containing values from ...

Collaborate on sharing CSS and TypeScript code between multiple projects to

I am looking for a solution to efficiently share CSS and TS code across multiple Angular projects. Simply copy-pasting the code is not an ideal option. Is there a better way to achieve this? ...

Design a model class containing two arrow functions stored in variables with a default value

I am looking to create a model class with two variables (label and key) that store functions. Each function should take data as an input object. If no specific functions are specified, default functions should be used. The default label function will retur ...

After the rendering process, the React Component member goes back to a state of

One issue I encountered is related to a component that utilizes a separate client for making HTTP requests. Specifically, when trying to use the client within a click event handler, the call to this.client.getChannel() fails due to this.client being undefi ...

Transforming TypeScript snapshot data in Firebase Cloud Functions into a string format for notification purposes

I've encountered the following code for cloud functions, which is intended to send a notification to the user upon the creation of a new follower. However, I'm facing an issue regarding converting the snap into a string in order to address the er ...

Receive regular updates every week for an entire month using Javascript

How can I calculate the number of check-ins per week in a month using Javascript? I have been unable to find relevant code for this task. Specifically, I am interested in determining the total count of user check-ins on a weekly basis. For example, if a u ...

Passing the array as query parameters and retrieving it using the angular getAll function is the most efficient way

When using this function, I extract the ids of items and aim to send them as an array for retrieval with getAll(). const queryParams: Record<string, string[]> = selectedItems.reduce( (acc, curr, index) => ({ ...acc, [&apo ...

Trigger a change event for a Material Checkbox by referencing its unique identifier

<div *ngFor="let cus of deselectedList | keyvalue" (click)="clickCheckBox('customer_'+cus.key+'_checkbox')"> {{cus.key}} <mat-checkbox id="customer_{{cus.key}}_checkbox" (change ...

How can I ensure that PrimeNG is functioning properly?

I'm encountering some difficulties setting up PrimeNG correctly to utilize its rich text editor. Despite installing all the necessary components, it's still not functioning as expected. https://i.stack.imgur.com/4kdGf.png This is my package.jso ...

Utilizing a component from a different module

Currently working on Angular 4 and facing an issue with referencing a component from another module. In my EngagementModule, the setup is defined as below: import { NgModule } from '@angular/core'; // other imports... @NgModule({ imports: [ ...