Adr*_*mew 14 unit-testing reactjs jestjs enzyme react-hooks
我有一个 React hooks 功能组件,我想用 Jest/Enzyme 测试它。我想根据 useState 值测试其第三级渲染行为。我似乎无法在网上找到任何示例。没有“点击”来模拟 - 没有 API 调用模拟,因为最后,我仍然需要根据 useState 值进行测试。过去,使用类组件,我可以设置状态。有了新的钩子,我就不能了。那么,基本上 - 如何在模拟的 SubmitForm 函数中模拟异步等待,以便渲染行为正常?
这是我的组件:
import React, { useState } from 'react';
import { Redirect } from 'react-router-dom';
import Form from 'core/Form';
export const Parent = ({submitForm}) => {
const [formValues, setFormValues] = useState({});
const [redirect, setRedirect] = useState(false);
const handleChange = name => evt => {
setFormValues({ ...formValues, [name]: evt.target.value });
};
const onSubmit = async () => {
try {
const res = await submitForm(formValues);
if (res) setRedirect(true);
else setRedirect(false);
} catch (err) {
console.log('Submit error: ', err);
}
};
return redirect ? (
<Redirect push to={path} />
) : (
<Form onSubmit={onSubmit} values={formValues} onChange={handleChange} />
);
};
export default Parent;
Run Code Online (Sandbox Code Playgroud)
这是我到目前为止的测试:
import React from 'react';
import { shallow } from 'enzyme';
import { Redirect } from 'react-router-dom';
import Parent from './Parent';
import Form from 'core/Form';
let wrapper, props;
.
.
.
describe('<Parent /> rendering', () => {
beforeEach(() => {
props = createTestProps();
wrapper = shallow(<Parent {...props} />);
});
afterEach(() => {
jest.clearAllMocks();
});
const setState = jest.fn();
const useStateSpy = jest.spyOn(React, 'useState');
useStateSpy.mockImplementation(init => [init, setState]);
it('Should render 1 Form', () => {
expect(wrapper.find(Form)).toHaveLength(1);
});
it('renders Redirect after API call', () => {
setRedirect = jest.fn(() => false);
expect(wrapper.find(Redirect)).toHaveLength(1);
});
it('renders Form before API call', () => {
setRedirect = jest.fn(() => true);
expect(wrapper.find(Form)).toHaveLength(1);
});
});
Run Code Online (Sandbox Code Playgroud)
sli*_*wp2 14
你不需要监视useState钩子。这意味着您不应该直接测试组件的这些钩子和方法。相反,您应该测试组件的行为(state以及props呈现的内容)
例如
\n\nindex.tsx:
import React, { useState } from \'react\';\nimport { Redirect } from \'react-router-dom\';\n\nexport const Form = ({ onSubmit, onChange, values }) => <form onSubmit={onSubmit}></form>;\nconst path = \'/user\';\n\nexport const Parent = ({ submitForm }) => {\n const [formValues, setFormValues] = useState({});\n const [redirect, setRedirect] = useState(false);\n\n const handleChange = (name) => (evt) => {\n setFormValues({ ...formValues, [name]: evt.target.value });\n };\n\n const onSubmit = async () => {\n try {\n const res = await submitForm(formValues);\n if (res) setRedirect(true);\n else setRedirect(false);\n } catch (err) {\n console.log(\'Submit error: \', err);\n }\n };\n\n return redirect ? (\n <Redirect push to={path} />\n ) : (\n <Form onSubmit={onSubmit} values={formValues} onChange={handleChange} />\n );\n};\n\nexport default Parent;\nRun Code Online (Sandbox Code Playgroud)\n\nindex.test.tsx:
import Parent, { Form } from \'./\';\nimport React from \'react\';\nimport { shallow } from \'enzyme\';\nimport { Redirect } from \'react-router-dom\';\nimport { act } from \'react-dom/test-utils\';\n\nconst whenStable = async () =>\n await act(async () => {\n await new Promise((resolve) => setTimeout(resolve, 0));\n });\n\ndescribe(\'60137762\', () => {\n it(\'should render Form\', () => {\n const props = { submitForm: jest.fn() };\n const wrapper = shallow(<Parent {...props}></Parent>);\n expect(wrapper.find(Form)).toBeTruthy();\n });\n\n it(\'should handle submit and render Redirect\', async () => {\n const props = { submitForm: jest.fn().mockResolvedValueOnce(true) };\n const wrapper = shallow(<Parent {...props}></Parent>);\n wrapper.find(Form).simulate(\'submit\');\n await whenStable();\n expect(props.submitForm).toBeCalledWith({});\n expect(wrapper.find(Redirect)).toBeTruthy();\n });\n\n it(\'should handle submit and render Form\', async () => {\n const props = { submitForm: jest.fn().mockResolvedValueOnce(false) };\n const wrapper = shallow(<Parent {...props}></Parent>);\n wrapper.find(Form).simulate(\'submit\');\n await whenStable();\n expect(props.submitForm).toBeCalledWith({});\n expect(wrapper.find(Form)).toBeTruthy();\n });\n\n it(\'should handle error if submit failure\', async () => {\n const logSpy = jest.spyOn(console, \'log\');\n const mError = new Error(\'network\');\n const props = { submitForm: jest.fn().mockRejectedValueOnce(mError) };\n const wrapper = shallow(<Parent {...props}></Parent>);\n wrapper.find(Form).simulate(\'submit\');\n await whenStable();\n expect(props.submitForm).toBeCalledWith({});\n expect(logSpy).toHaveBeenCalledWith(\'Submit error: \', mError);\n });\n});\nRun Code Online (Sandbox Code Playgroud)\n\n带有覆盖率报告的单元测试结果:
\n\n PASS stackoverflow/60137762/index.test.tsx\n 60137762\n \xe2\x9c\x93 should render Form (18ms)\n \xe2\x9c\x93 should handle submit and render Redirect (15ms)\n \xe2\x9c\x93 should handle submit and render Form (8ms)\n \xe2\x9c\x93 should handle error if submit failure (18ms)\n\n console.log node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866\n Submit error: Error: network\n at /Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:39:20\n at step (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:44:23)\n at Object.next (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:25:53)\n at /Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:19:71\n at new Promise (<anonymous>)\n at Object.<anonymous>.__awaiter (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:15:12)\n at Object.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:37:47)\n at Object.asyncJestTest (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:100:37)\n at resolve (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:43:12)\n at new Promise (<anonymous>)\n at mapper (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:26:19)\n at promise.then (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:73:41)\n\n-----------|---------|----------|---------|---------|-------------------\nFile | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s \n-----------|---------|----------|---------|---------|-------------------\nAll files | 78.57 | 100 | 40 | 93.75 | \n index.tsx | 78.57 | 100 | 40 | 93.75 | 12 \n-----------|---------|----------|---------|---------|-------------------\nTest Suites: 1 passed, 1 total\nTests: 4 passed, 4 total\nSnapshots: 0 total\nTime: 3.716s, estimated 5s\nRun Code Online (Sandbox Code Playgroud)\n\n源代码:https://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/60137762
\n| 归档时间: |
|
| 查看次数: |
68575 次 |
| 最近记录: |