How can I playfully imitate an exported function to make it return a Promise instead of undefined?

In my project, I am using jest along with Typescript. There is a function called "processData" that is exported from a service file...

export async function processData(
  data: MyDataI,
): Promise<
    ...

In another file (run-my-process.ts) which is called via npm cli, the "processData" function is imported like this:

import {processData} from '../services/my.service';
...
         processData(data)
            .then((result) => {

I wanted to mock the "processData" function in jest, so I attempted this:

jest.mock('../services/my.service', () => {
  // The mock returned only mocks the generateServerSeed method.
  const actual = jest.requireActual('../services/my.service');

  return {
      ...actual,
     processData: jest.fn().mockReturnValue(Promise.resolve({
        dataInserted: 1,
      }))
    }
});

...

describe('calls the job', function () {


  it('invokes the function', async () => {
    ...

    jest.spyOn(process, 'exit').mockImplementationOnce(() => {
      throw new Error('process.exit() was called.');
    });

    expect(() => {
      require('./run-my-process');
    }).toThrow('process.exit() was called.');

However, the test fails and throws the error:

ERROR [1624482717612] (71310 on localhost): Cannot read property 'then' of undefined

It seems like the function "processData" is being evaluated as "undefined" when called with an argument. What is the correct way to mock this function to return a Promise and make my Jest test pass?

Answer №1

An alternative method that is much simpler involves:

import * as myService from '../services/my.service';

jest.spyOn(myService, 'processData').mockResolvedValue({ dataInserted: 1 });

By utilizing this approach, you can avoid the complicated mocking process typically done with jest.mock()

Answer №2

Check out this informative article here, which provides guidance on how to adapt data as needed.

processData: jest.fn(() => Promise.resolve({ data: {}}))

Make sure to export the code snippet below:

export default jest.fn(() => Promise.resolve({ data: {} }));

Answer №3

To utilize jest.fn, you can supply it with a function that returns a promise:

jest.mock('../services/my.service', () => ({
  ...jest.requireActual('../services/my.service'),
  processData: jest.fn(() => Promise.resolve({ dataInserted: 1 }))
}));

If you require the ability to alter the resolved value for each test, you have the option of resolving a variable within the outer scope that can be modified, or mocking the resolved value using .mockResolvedValue or .mockResolvedValueOnce in the test.

Resolve a modifiable variable:

const newData = {
  dataInserted: 1,
};

jest.mock('../services/my.service', () => ({
  ...jest.requireActual('../services/my.service'),
  processData: jest.fn(() => Promise.resolve({ data: newData }))
}));

describe('...', () => {
  beforeEach(() => {
    newData.dataInserted = 100;
  });

  it('...', () => {
  });
});

Mock the resolved value:

import { processData } from '../services/my.service';

jest.mock('../services/my.service', () => ({
  ...jest.requireActual('../services/my.service'),
  processData: jest.fn()
}));

describe('...', () => {
  beforeEach(() => {
    (processData as jest.Mock).mockResolvedValueOnce({ dataInserted: 100 });
  });

  it('...', () => {
  });
});

Answer №4

There are some issues with certain responses, particularly when it comes to the typing of the mocking function in Typescript.

For those working with typescript and ts-jest, consider using this approach with mocked

import { mocked } from "ts-jest/utils";
import { processData } from '../services/my.service';
jest.mock("../services/my.service");
const mockedProcessData = mocked(processData, true); // ensuring correct typing, I personally prefer mocking functions outside tests but you can also use mocked directly within your tests.

// sample test
mockedProcessData.mockResolvedValueOnce({
    dataInserted: 1,
})

Answer №5

I tend to steer clear of using jest.mock function due to issues with typings and implicitness. I have also encountered unexpected behavior at times, as jest.mock can feel like a magical solution that should only be used in rare cases where direct access to the source code is not possible.

To keep your code clean and explicit, consider implementing the dependency inversion pattern:

Service code:

export const runProcess = (mapper: (data: Data) => Promise<string>) => {
  // implementation
}

Unit test code:

it('should work', () => {
  const mapper = jest.fn(() => Promise.resolve('testing'));


  expect(runProcess(mapper)).toEqual(...);

})

Client code:

import { runProcess } from './run-process';
import { dataMapper } from './data-mapper';

runProcess(dataMapper)

Alternatively, you can set the data mapper as a default value in your code:

import { dataMapper, DataMapper } from './data-mapper';
export const runProcess = (mapper: DataMapper = dataMapper) => {
  // implementation
}

This way, you won't need to pass the mapper everywhere in your code, although it does introduce a dependency that should ideally be tested. Lastly, ask yourself: Can you write at least one integration test? Integration tests are important to complement unit tests for overall testing efficiency.

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

Utilize generic types as object properties in TypeScript

Is there a way to achieve something similar in TypeScript like the following: export type CoordinateSelector = <T>(d: Coordinate) => d[T]; export interface LinkVerticalLineProps { x: CoordinateSelector<'x'>; y: CoordinateSele ...

What is the reason why modifying a nested array within an object does not cause the child component to re-render?

Within my React app, there is a page that displays a list of item cards, each being a separate component. On each item card, there is a table generated from the nested array objects of the item. However, when I add an element to the nested array within an ...

What are the conditions for Jasmine's .toHaveBeenCalledWith to match the parameters?

I'm working on an Angular service along with its Jasmine test. The test is calling f1() and spying on f2(). The function f2 takes a variable v2 and updates it by setting field 'a' to 3. I expected the function f2 to be called with v2 (as def ...

Using the TranslateService in Angular to externalize an array of strings

I am new to externalizing code. As I was working on developing a month picker in Angular, I initially had an array of months with hardcoded names in my typescript file: arr = ['Jan', 'Feb', 'Mar', 'Apr', 'May&a ...

What is the best way to create an ESM / CJS module without needing to include '.default' in the imports?

Is it possible to configure my package.json to support different imports for CJS and ESM formats? "main": "dist/cjs/index.js", "module": "dist/esm/index.js", In addition, can I set up my TypeScript compiler to g ...

NextJS and Context API throwing a Typescript error

I've been working on my _app.tsx file and here's the code snippet I have: import React from 'react' import type { AppProps } from 'next/app' /* Import Styles */ import '@themes/index.scss' /* Import Template */ imp ...

Tips for rendering arrays as JSX elements in React.js

In this case, the names of users are stored in an array called "users" which is split from the photo array. I want to display the results from the array as buttons. The code snippet for this is provided below: let users: string[] = []; const setUserNames ...

Step-by-step guide on integrating a custom JS file into an Angular component

Trying to grasp Angular, I embarked on adding some JavaScript logic to one of my components from a separate JS file. While following advice from a similar query (How to add custom js file to angular component like css file), it seems I missed something cru ...

The unit tests designed to verify the return of a View are currently unsuccessful

Encountering issues with .Net 4.5 MVC application when running unit tests that check for View return, as they are failing on the build server. The error message states a failure to locate System.Web.WebPages: Error Message: Test method CCSSWorkflow.Tests ...

Exploring various options for identifying keys

I have a specific object structure that I receive, and it looks like this: const object = { a: 10, an: 20 }; This object has predefined keys taken from another source: const keys = { /** * A long name that describes what "a" stands for. */ ...

Building a custom user authentication system using Angular, Firebase, and Google authentication

Recently, I came across this video (in code), and implemented my Auth Service based on the example provided. However, my User Interface structure is slightly different: interface User { uid: string; email: string; photoURL: string; displayName: st ...

Utilizing the "as" keyword for type assertion in a freshly created react application using create-react-app leads to the error message `Parsing error: Unexpected token, expected ";"`

After creating a new CRA project using yarn create react-app my-app --template typescript, I encountered an error when trying to run the development server with yarn start: src/App.tsx Line 5:24: Parsing error: Unexpected token, expected ";" ...

Angular directive to access HTML document attributes

I have been trying to access the HTML document using an HTML object. After searching online, I learned that I could add providers: [{ provide: "windowObject", useValue: window}] in the app.module file. Then, within my app, I can inject the win ...

Can one determine the type of an object that is inherited from another object?

I have various classes defined as follows: export A {...} export B {...} export C {...} export type data = A | B | C; Next, I need to work with an array of data like this: dataArr : Array<data> ; Is there a way to use something similar to type of ...

The `setState` function is failing to change the current value

I'm having an issue with setting State in the dropdown component of semantic-ui-react while using TypeScript in my code. The selected category value is always returning an empty string "". Any suggestions on how to resolve this problem? impo ...

Leveraging jest for handling glob imports in files

My setup involves using webpack along with the webpack-import-glob-loader to import files utilizing a glob pattern. In one of my files (src/store/resources/module.ts), I have the following line: import '../../modules/resources/providers/**/*.resource. ...

Issue: (SystemJS) Unable to find solutions for all parameters in $WebSocket: ([object Object], [object Object], ?)

Upon running the code snippet below, an error is thrown: Error: (SystemJS) Can't resolve all parameters for $WebSocket: ([object Object], [object Object], ?). app.component.ts import { Component } from '@angular/core'; import {$WebSocket} ...

Incorporating a complex React (Typescript) component into an HTML page: A Step-by

I used to have an old website that was originally built with Vanilia Javascript. Now, I am in the process of converting it to React and encountering some issues. I am trying to render a compound React(Typescript) component on an HTML page, but unfortunatel ...

Testing Django database routers can be a challenge, especially when it comes to migrations. How can you ensure they are

After going through the documentation on database routers in Django 2.2, I have a decent understanding of the concept. However, I find implementing it in practice a bit challenging. When faced with complex and interdependent situations, my go-to approach ...

Material-UI: Error thrown when attempting to pass props to makeStyles in React due to missing property 'X' on type '{}'

Currently experimenting with Adapting based on props, you can find more information here import React from 'react'; import { makeStyles } from '@material-ui/core'; const useStyles = makeStyles({ // style rule foo: props => ( ...