Mocking a third-party callback function in Jest for method implementation

Utilizing Nest + Cognito for user authentication in an application, I have a method within my Authentication service that requires testing/mocking:


async cognitoRegister(userPool: CognitoUserPool, {
    name,
    password,
    email
}: AuthRegisterInput): Promise < ISignUpResult > {
    return new Promise((resolve, reject) => {
        return userPool.signUp(
            name,
            password,
            [new CognitoUserAttribute({
                Name: 'email',
                Value: email
            })],
            null,
            (err, result) => {
                if (!result) {
                    reject(err);
                } else {
                    resolve(result);
                }
            },
        );
    });
}

The signUp function is from the third-party CognitoUserPool which was successfully mocked using module name mapper in my package.json as shown below:

function CognitoUserPool(data) {
  const { UserPoolId, ClientId } = data;
  this.userPoolId = UserPoolId;
  this.clientId = ClientId;
  this.getCurrentUser = jest.fn().mockReturnValue("cognitouserpool");
  // This method
  this.signUp = jest.fn().mockReturnValue(true);
}
module.exports = CognitoUserPool;

Its implementation:

module.exports = {
  CognitoUserPool: jest.fn().mockImplementation(require("./CognitoUserPool")),
};

Since signUp method accepts a callback to provide a result or rejection value, mocking it is essential to prevent Jest timeout errors due to the pending Promise.

I am essentially trying to mock a function like so:


const foo = (arg1, cb) => {
    ...do something...
}

const bar = (arg1, arg2...) => {
    return new Promise((resolve, reject) => {
        return foo(arg1, (err, result) => {
            if (!result) {
                reject(err)
            } else {
                resolve(result)
            }
        })
    })
}

This is what I am attempting in my test:

it("should register a cognito user", async () => {
  const mockedCongitoUserPool = new CognitoUserPool({
    UserPoolId: authConfig.userPoolId,
    ClientId: authConfig.clientId,
  });
  const result = await service.cognitoRegister(mockedCongitoUserPool, {
    ...mockedUser,
  });
  console.log(result);
});

For more details, refer to these git links:

Main service link

Mocked third party implementation link

Tests implementation link

Your assistance is greatly appreciated! Feel free to ask for further clarification, as I could really use some help with this. <3

Answer №1

In order to achieve a static resolution, it is important for your mocked module to define an implementation like the one shown below instead of just a simple return value:

this.registerUser = jest.fn().mockImplementation((name, email, password, callback) => {
  process.nextTick(callback(null, 'user registered successfully'))
});

The use of process.nextTick here simply simulates asynchronous behavior, but you could also use callback(null, 'some result') directly if that suffices.

If you need to have dynamic control over the callback resolution, you have the option to override the default mocked implementation based on the specific scenario:

let newUser: User;
beforeAll(() => { // or perhaps beforeEach, depending on what the mock entails/does
  newUser = new User({ username: 'testUser', email: 'test@example.com' });
});
it('should succeed', async () => {
  const expectedResponse = { success: true };
  newUser.registerUser.mockImplementation((a, b, c, cb) => cb(null, expectedResponse));
  await expect(userService.register(newUser)).resolves.toBe(expectedResponse);
});
it('should fail', async () => {
  const expectedError = new Error('registration failed');
  newUser.registerUser.mockImplementation((a, b, c, cb) => cb(expectedError));
  await expect(userService.register(newUser)).rejects.toThrow(expectedError);
});

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

Unable to get the deletion functionality to work for Dropzone.js and PHP script

I am currently using dropzone to handle file uploads. My goal is to delete the files on the server when the user clicks on the removeLink. I have implemented an Ajax function that calls a .php site for this purpose. However, I am facing an issue where I am ...

Why is my Laravel function retrieving outdated session data?

Currently, I am in the process of developing a shopping cart feature using session. My goal is to create a function that can update the quantity of an item in the shopping cart and refresh both the subtotal and the list of items in the cart displayed on th ...

Sharing information from Directive to Component in Angular

Here is a special directive that detects key strokes on any component: import { Directive, HostListener } from '@angular/core'; @Directive({ selector: '[keyCatcher]' }) export class keyCatcher { @HostListener('document:keydo ...

An error occured: Unable to access undefined properties (specifically 'hasOwnProperty')

I encountered an issue and managed to solve it. I am currently working on creating an update profile page for my Express app using Mongoose, but I keep getting the error "TypeError: Cannot read properties of undefined (reading 'hasOwnProperty')". ...

Enhance the appearance of TreeGrid nodes by customizing the icons according to the data within

Currently, I am working with the MUI DataGridPro component and my goal is to customize the icons of the TreeGrid nodes based on the data. This image illustrates what I hope to achieve: https://i.stack.imgur.com/nMxy9.png Despite consulting the official do ...

Is it time to advance to the next input field when reaching the maxLength?

In my Vue form, I have designed a combined input field for entering a phone number for styling purposes. The issue I am facing is that the user needs to press the tab key to move to the next input field of the phone number. Is there a way to automaticall ...

Retrieve a particular element from an array within a JSON object using Ionic

I am currently facing a challenge in extracting a specific array element from a JSON response that I have retrieved. While I can successfully fetch the entire feed, I am struggling to narrow it down to just one particular element. Here is what my service ...

Styling Process Steps in CSS

Just starting out with CSS! I'm looking to replicate the Process Step design shown in the image. https://i.stack.imgur.com/Cq0jY.png Here's the code I've experimented with so far: .inline-div{ padding: 1rem; border: 0.04rem gray ...

Join a subscription and remain subscribed in sequential order

Within the code below, there is a nested subscribe function. It takes a schedule_id and retrieves questions based on that schedule_id. The functionality works correctly, but the order in which getQuestion() is executed is not guaranteed. Schedule IDs: 111, ...

Tips for avoiding an automatic slide up in a CSS menu

Is there a way to disable the automatic slide-up effect on my CSS menu when clicking a list item? I have tried using stop().slideup() function in my JavaScript code, but it doesn't seem to work. Additionally, I would like to highlight the selected lis ...

After making a change to a Vue or JavaScript file, running `npm run watch` causes a crash due to the `compileTemplate` function now requiring

I am facing an issue where both npm run dev and prod are functioning correctly, but when I attempt to run watch and make changes to files, npm run watch throws an error and crashes. I am using laravel mix with TypeScript, and my webpack mix parameters are ...

Exploring methods to verify a service subscription to a topic provided by a different service

My services provide the following functionalities: @Injectable({ providedIn: 'root' }) export class Service1 { dataHasChanged = new Subject(); private data1; private data2; constructor() {} getData() { return { data1: th ...

Creating a dynamic dropdown menu to display nested arrays in Vuejs

I have some important data https://i.sstatic.net/Cq2t6.png Challenge I'm facing difficulty in accessing the tubes array within my data Solution script data() { return { types: [] } }, m ...

Using jQuery to insert PHP code into a <div> element

In our capstone project, I'm looking to implement a dropdown calendar feature. I initially attempted to use ajax within a dropdown Bootstrap 4, but encountered issues with collapsing. As an alternative, I am considering utilizing the include method. A ...

Most efficient method for nesting child objects within an array of objects using JavaScript

Hey experts, I'm on the hunt for the most efficient method to transform this data: [ { "id": 1, "animal": "cat", "age": 6, "name": "loky" }, { "id": 2, "animal": &quo ...

Looping the Connection between Socket.io and Node

I have encountered a problem with my Unity client connecting to my node server using socket.io. While the initial connection is successful and acknowledged, when I try to emit a message to the connected client, the connection seems to get reopened as if a ...

Encountering a TypeScript error when attempting to utilize indexOf on a Typed Array, leading to restriction

I have been working with an Interface, where I created an array of type Interface. I am currently facing some IDE error complaints when trying to use the .indexOf method on the Array. These errors seem confusing to me, and I'm hoping someone here migh ...

Exploring Ember Octane (version 3.22 and above): benefits of using {{on 'click' this.function}} over traditional onclick={{this.function}} method

When working with Ember Octane, there are two different ways to attach a function to an event in an hbs file. The first way is the EmberJS approach: {{on 'click' this.function}} Alternatively, you can use the classic HTML method: onclick={{this ...

Ways to change a value into int8, int16, int32, uint8, uint16, or uint32

In TypeScript, the number variable is floating point by default. However, there are situations where it's necessary to restrict the variable to a specific size or type similar to other programming languages. For instance, types like int8, int16, int32 ...

Vite terminal not executing Npm commands

Here is what I entered: npm run dev Error: npm ERR! Missing script: "dev" npm ERR! npm ERR! To see a list of scripts, run: npm ERR! npm run npm ERR! A complete log of this run can be found in: npm ERR! C:\Users\andre\AppData&bs ...