-1

I'm trying to mock the methods & hooks in a file and import those mock functions as I need them, 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 & 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 bare minimum hook which just returns a value(state).

i'm importing the mocks variable in my test file and calling mocks.useMyHook(), but it doesn't work. I still get the original hook's value in return while testing.

I'm testing a Hello.jsx component which uses useMyHook hooks and logs its return value in 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 console, instead of the mock value(12345).

I found out that using jest.mock before the describe instead of jest.spyOn works. But I have a lot of tests that change the mocked value during the tests(like inside a specific test() block) by calling mocks.useMyHook and the hook would return another value for that specific test cases. The spyOn method used to work previously fine, but alot of things changed in the project so now I'm unable to make it work.

I'm on a next js project. Here's some info on 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.

  • You're spying on the property of an object that _only exists_ to have its property spied on, and is accessible from anywhere else (tests or otherwise). – jonrsharpe May 10 '23 at 13:20
  • @jonrsharpe sorry i dont understand what you're implying here. the `useMyHook` file has a react hook defined like `const useMyHook = () => { ... }` and exported as `export { useMyHook }` in that file. I'm just expecting that hook to be mocked by jest. – javascript-wtf May 10 '23 at 14:35
  • `jest.spyOn({ useMyHook }, "useMyHook").mockImplementation(() => ({ val }));` creates an object and spies on it, but that object has _no connection_ to anything else happening in your program or its tests. – jonrsharpe May 10 '23 at 15:10
  • once i do `jest.spyOn` like that, won't that mock the hook to return the mocked value, anywhere that it is used? i've added the component and the hooks code as well in the post. – javascript-wtf May 10 '23 at 15:26

1 Answers1

0

just updating the jest.config.js's transform property solved the issue.

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

i needed to add mjs as well, to the transform patterns.

Hope this helps someone.