The challenge of mocking methods/hooks remains when utilizing `jest.spyOn` in Jest

I am attempting to create mock methods and hooks in a file, then import those mock functions as needed in my test files.

useMyHook.jsx

const useMyHook = () => {
  const [val, setVal] = useState(200)

  return { val }
}

export { useMyHook }

Hello.jsx:

import { useTestHook } from "@/hooks/useTestHook";

export const Hello = () => {
  const { val } = useTestHook();
  console.log({ val });

  return (
    <div>
      <h2>Hello</h2>
    </div>
  );
};

This is the file where I mock the hooks and functions. mock-hooks.js:

import { useMyHook } from "@/hooks/useMyHook";
// import * as Hook from "@/hooks/useMyHook";

const mocks = {
  useMyHook: (val = 9999) => {
    jest
      .spyOn({ useMyHook }, "useMyHook")
      .mockImplementation(() => ({ val }));

    /** this method of mocking used to work before, but now it gives 
        `TypeError: Cannot redefine property: useMyHook` */
    // jest.spyOn(Hook, "useMyHook")
    //   .mockImplementation(() => ({ val }));

  },
};

export { mocks };

The useMyHook hook is a simple hook that only returns a value (state).

I import the mocks variable in my test file and call mocks.useMyHook(), but it does not work. I still receive the original hook's value while testing.

I am testing a Hello.jsx component that uses the useMyHook hooks and logs its return value in the console.

Hello.test.jsx:

import { mocks } from "@/utils/test/mock-hooks";

import { Hello } from "./Hello";
import { render } from "@testing-library/react";

describe("Hello test", () => {
  beforeEach(() => {
    mocks.useMyHook(12345);
    render(<Hello />, {
      wrapper: ({ children }) => <>{children}</>,
    });
  });

  test("should render the main container", () => {
    screen.debug()
  });
});

When I run the test, I still see the original hook's value being logged in the console instead of the mock value (12345).

I discovered that using jest.mock before the describe instead of jest.spyOn works. However, I have many tests that modify the mocked value during the tests (such as inside a specific test() block) by calling mocks.useMyHook and the hook would return a different value for those specific test cases. The spyOn method used to work fine previously, but many changes have been made in the project so now I am unable to make it work.

I am working on a Next.js project. Here is some information about the project:

Dependencies:

{
  ...
 "dependencies": {
    "next": "12.3.0",
    "react": "17.0.2",
    "react-dom": "17.0.2"
  },
  "devDependencies": {
    "@babel/core": "7.17.8",
    "@testing-library/jest-dom": "5.16.4",
    "@testing-library/react": "12.1.2",
    "@testing-library/react-hooks": "7.0.2",
    "babel-jest": "27.4.6",
    "jest": "28.1.0",
    "jest-environment-jsdom": "28.1.0"
  }
}

jest.config.js:

const nextJest = require("next/jest");

const createJestConfig = nextJest({});

const customJestConfig = {
  collectCoverageFrom: ["**/*.{js,jsx,ts,tsx}"],
  verbose: true,
  modulePaths: ["<rootDir>/"],
  modulePathIgnorePatterns: ["<rootDir>/.next"],
  testPathIgnorePatterns: ["<rootDir>/node_modules/", "<rootDir>/.next/"],
  transform: {
    "^.+\\.(js|jsx|ts|tsx)$": ["babel-jest", { presets: ["next/babel"] }],
  },
  transformIgnorePatterns: [
    "/node_modules/",
    "^.+\\.module\\.(css|sass|scss)",
  ],
  testEnvironment: "jsdom",
  moduleNameMapper: {
    "^@/(.*)$": "<rootDir>/$1",
  },
};

module.exports = createJestConfig(customJestConfig);

.babelrc:

{
  "presets": ["next/babel"],
  "plugins": []
}

EDIT: Typo & Added useMyHook hook & Hello component code.

Answer №1

By simply adjusting the transform property in the jest.config.js, the issue was successfully resolved.

transform: {
    '^.+\\.(js|jsx|ts|tsx|mjs)$': ['babel-jest', { presets: ['next/babel'] }]
  },

In order to resolve the issue, I found it necessary to include mjs in the transform patterns as well.

Hopefully this information will be beneficial to someone facing a similar problem.

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

import the JSONP file from the local directory and assign it to a variable on

My current setup involves a local webpage that is loading a large JSON database file by utilizing a file named data.jsonp that contains data={a JSON dictionary of information}. I then import this file in my HTML using <script src="data.jsonp"& ...

Challenges in Implementing Shadows with Animations in ThreeJS MeshDepthMaterial

I'm facing an issue where casting shadows through transparent parts of my Mesh using the MeshDepthMaterial causes the shadows of animated objects to stop moving along with the animation. You can see an example of this problem here: https://jsfiddle.n ...

Changing the bootstrap popover location

I'm looking to customize the position of a Bootstrap popover that appears outside of a panel. Here's my setup: HTML: <div class="panel"> <div class="panel-body"> <input type="text" id="text_input" data-toggle="popover ...

SPRING --- Tips for sending an array of objects to a controller in Java framework

Can someone help me with this issue? I am using AngularJS to submit data to my Spring controller: @RequestParam(value = "hashtag[]") hashtag[] o The above code works for array parameters but not for an array object. This is my JavaScript script: $http ...

What methods can be used to control the URL and back button in a web application designed similar to an inbox in Gmail?

Similar Question: Exploring AJAX Techniques in Github's Source Browser In my application, there are essentially 2 main pages: The main page displays a list of applications (similar to an inbox) The single application page is packed with various ...

Does Eslint's no-restricted-imports rule only limit imports from the package's root directory?

For example, I'm looking to limit the usage of importing react-use, while still permitting react-use/lib/usePrevious. I attempted this: rules: { 'no-restricted-imports': [2, { patterns: [ 'react-use', ] }], }, As ...

I am attempting to establish a connection with the Converge Pro 2 system from Clearone using NodeJS node-telnet-client, but unfortunately, my efforts to connect have been unsuccessful

My connection settings are as follows: { host: '192.168.10.28', port: 23, shellPrompt: '=>', timeout: 1500, loginPrompt: '/Username[: ]*$/i', passwordPrompt: '/Password: /i', username: 'clearone ...

Creating an HTML layout or template directly within a JavaScript bookmarklet

Currently, I am using a bookmarklet that generates a user interface for interaction. The method I have been using involves creating elements using $('<element>').addClass().css({..});, but this approach is proving to be difficult to maintai ...

Using innerHTML in React to remove child nodes Tutorial

I'm encountering slow performance when unmounting over 30,000 components on a page using conditional rendering triggered by a button click. This process takes around 10+ seconds and causes the page to hang. Interestingly, setting the parent container& ...

What is the best strategy for managing pagination when dealing with a large number of pages?

Up until now, I have understood that pagination only links different pages together. This works fine when dealing with a limited number of pages or posts to display. However, what if I have 30 or more pages to paginate? Wouldn't it be impractical to c ...

What is the purpose of passing functions down to components in React?

It's interesting to think about why we choose to define all component functions in one central location, such as index.js, and then pass them down. Is there a good reason for this approach? For instance, if I need to create a click handler for a list ...

JavaScript is claiming that my class method is invalid, despite the fact that it is obviously a valid function

Implementing a genetic algorithm using node has been my latest challenge. After browsing through the existing questions on this topic, I couldn't find anything relevant to my issue. The problem arises when I attempt to call the determine_fitness metho ...

Using JSON input to add color to a d3 bullet chart

I am currently working with a D3 bullet chart example and trying to enhance it by incorporating different colors for the ranges directly into the JSON file. The link to the original example can be found here: . I need this customization because I require d ...

What is a dynamic route that does not include a slash?

I currently have a Next.js application set up with an SSR component that includes routes like /product1232312321. Previously, my solution was to create two folders: one named product and another nested folder inside it called [id]. Then I would redirect f ...

What is the best method for retrieving a child control from a TR element?

I am facing an issue with a hidden field inside each Tr in my template: <ItemTemplate> <tr style="" class="ui-selectee trClass ui-widget-content"> <td style="width: 100px"> <asp:HiddenField ID="idField" runat=" ...

Displaying a message when there are no results in Vue.js using transition-group and encountering the error message "children must be keyed

Utilizing vue.js, I crafted a small data filter tool that boasts sleek transitions for added flair. However, I encountered an issue with displaying a message when there are no results matching the current filters. Here's what I attempted: <transit ...

What is the best way to find a match for {0} while still allowing for proper

I am working on developing a text templating system that allows for defining placeholders using {0}, similar to the functionality of .Net's string.format method. Here is an example of what I am aiming for: format("{0}", 42), // output ...

Combine filter browsing with pagination functionality

I came across a pagination and filter search online that both function well independently. However, I am looking to merge them together. My goal is to have the pagination display as << [1][2] >> upon page load, and then adjust to <<[1]> ...

Utilizing a While Loop for SQL Queries in a Node.js Environment

So, I was attempting to iterate through an array using a while loop. I was able to successfully print a result from the SQL connection without the while loop, confirming that the query is working. However, when I tried to implement the same query within a ...

Is it possible for a route's URL in ui-router to be at the same level as another state while also utilizing $stateParams?

In my application, I want to implement a feature where hitting the same level of the URL will lead to either the baz state or the biz state with a parameter. angular.module('foo') .config(function($stateProvider){ $stateProvider .state(&apos ...