英文:
jest: unable to mock methods/hooks using `jest.spyOn`
问题
I'll provide the translation of the relevant code parts you provided:
我正在尝试模拟文件中的方法和钩子,并根据需要导入这些模拟函数到我的测试文件中。
`useMyHook.jsx`
```javascript
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>
);
};
这是我模拟钩子和函数的文件。
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 }));
/** 这种模拟的方法以前有效,但现在会出现“TypeError: 无法重新定义属性: useMyHook”错误 */
// jest.spyOn(Hook, "useMyHook")
// .mockImplementation(() => ({ val }));
},
};
export { mocks };
useMyHook
钩子是一个最简单的钩子,只返回一个值(状态)。
我在我的测试文件中导入 mocks
变量并调用 mocks.useMyHook()
,但它不起作用。在测试时,我仍然得到原始钩子的值。
我正在测试一个使用 useMyHook
钩子的 Hello.jsx
组件,该组件在控制台中记录其返回值。
Hello.test.jsx
:
import { mocks } from "@/utils/test/mock-hooks";
import { Hello } from "./Hello";
import { render } from "@testing-library/react";
describe("Hello 测试", () => {
beforeEach(() => {
mocks.useMyHook(12345);
render(<Hello />, {
wrapper: ({ children }) => <>{children}</>,
});
});
test("应该渲染主容器", () => {
screen.debug()
});
});
当我运行测试时,我仍然看到原始钩子的值被记录在控制台中,而不是模拟值(12345)。
我发现在 describe
之前使用 jest.mock
而不是 jest.spyOn
能够解决问题。但我有很多测试,在测试过程中会改变模拟的值(例如,在特定的 test()
块内),通过调用 mocks.useMyHook
钩子会使该钩子返回另一个值以用于特定的测试用例。以前使用的 spyOn
方法能够正常工作,但是项目中发生了很多变化,所以我无法使其工作。
我在一个 Next.js 项目上工作。以下是项目的一些信息:
依赖项:
{
...
"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": []
}
编辑:修正了拼写错误并添加了 useMyHook
钩子和 Hello
组件的代码。
<details>
<summary>英文:</summary>
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`
```javascript
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.
答案1
得分: 0
只更新jest.config.js
中的transform
属性解决了这个问题。
transform: {
'^.+\\.(js|jsx|ts|tsx|mjs)$': ['babel-jest', { presets: ['next/babel'] }]
},
我也需要添加mjs
到转换模式中。
希望对某人有所帮助。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论