如何通过更改useState测试React Hooks组件 [英] How can I test a React Hooks component by changing useState

查看:117
本文介绍了如何通过更改useState测试React Hooks组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个我想用Jest/Enzyme测试的React hooks功能组件.我想根据useState值测试其第三次渲染行为.我似乎无法在网上找到任何示例.没有点击"可以模拟-没有API可以模拟,因为最后,我仍然需要根据useState值进行测试.过去,使用类组件,我可以设置状态.有了新的挂钩,我做不到.那么,基本上-我该如何在模拟的SubmitForm函数中模拟异步等待,以便渲染正常运行?

I have a React hooks functional component that I'd like to test with Jest/Enzyme. I would like test its tertiary render behaviour based upon a useState value. I can't seem to find any example online. There is no 'click' to simulate - no API call to mock because at the end, I still need to test based upon the useState value. In the past, with class components, I could set the state. With the new hooks, I can't. So, basically - how do I mock an async await inside a mocked submitForm function so that the render behaves properly?

这是我的组成部分:

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;

到目前为止,这是我的测试内容

Here's my testing so far:

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);
  });
});

推荐答案

您不需要监视 useState 钩子.这意味着您不应该直接测试组件的这些挂钩和方法.相反,您应该测试组件的行为(状态 props 和呈现的内容)

You don't need to spy useState hook. Which means you should not test these hooks and methods of the component directly. Instead, you should test components' behavior(the state, props and what is rendered)

例如

index.tsx :

import React, { useState } from 'react';
import { Redirect } from 'react-router-dom';

export const Form = ({ onSubmit, onChange, values }) => <form onSubmit={onSubmit}></form>;
const path = '/user';

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;

index.test.tsx :

import Parent, { Form } from './';
import React from 'react';
import { shallow } from 'enzyme';
import { Redirect } from 'react-router-dom';
import { act } from 'react-dom/test-utils';

const whenStable = async () =>
  await act(async () => {
    await new Promise((resolve) => setTimeout(resolve, 0));
  });

describe('60137762', () => {
  it('should render Form', () => {
    const props = { submitForm: jest.fn() };
    const wrapper = shallow(<Parent {...props}></Parent>);
    expect(wrapper.find(Form)).toBeTruthy();
  });

  it('should handle submit and render Redirect', async () => {
    const props = { submitForm: jest.fn().mockResolvedValueOnce(true) };
    const wrapper = shallow(<Parent {...props}></Parent>);
    wrapper.find(Form).simulate('submit');
    await whenStable();
    expect(props.submitForm).toBeCalledWith({});
    expect(wrapper.find(Redirect)).toBeTruthy();
  });

  it('should handle submit and render Form', async () => {
    const props = { submitForm: jest.fn().mockResolvedValueOnce(false) };
    const wrapper = shallow(<Parent {...props}></Parent>);
    wrapper.find(Form).simulate('submit');
    await whenStable();
    expect(props.submitForm).toBeCalledWith({});
    expect(wrapper.find(Form)).toBeTruthy();
  });

  it('should handle error if submit failure', async () => {
    const logSpy = jest.spyOn(console, 'log');
    const mError = new Error('network');
    const props = { submitForm: jest.fn().mockRejectedValueOnce(mError) };
    const wrapper = shallow(<Parent {...props}></Parent>);
    wrapper.find(Form).simulate('submit');
    await whenStable();
    expect(props.submitForm).toBeCalledWith({});
    expect(logSpy).toHaveBeenCalledWith('Submit error: ', mError);
  });
});

带有覆盖率报告的单元测试结果:

Unit test results with coverage report:

 PASS  stackoverflow/60137762/index.test.tsx
  60137762
    ✓ should render Form (18ms)
    ✓ should handle submit and render Redirect (15ms)
    ✓ should handle submit and render Form (8ms)
    ✓ should handle error if submit failure (18ms)

  console.log node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866
    Submit error:  Error: network
        at /Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:39:20
        at step (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:44:23)
        at Object.next (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:25:53)
        at /Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:19:71
        at new Promise (<anonymous>)
        at Object.<anonymous>.__awaiter (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:15:12)
        at Object.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/60137762/index.test.tsx:37:47)
        at Object.asyncJestTest (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:100:37)
        at resolve (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
        at new Promise (<anonymous>)
        at mapper (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
        at promise.then (/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/node_modules/jest-jasmine2/build/queueRunner.js:73:41)

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |   78.57 |      100 |      40 |   93.75 |                   
 index.tsx |   78.57 |      100 |      40 |   93.75 | 12                
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        3.716s, estimated 5s

源代码: https://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/60137762

这篇关于如何通过更改useState测试React Hooks组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆