如何在带有Router的useEffect钩子的React中对React react-router组件的单元测试中更改路由

时间:2019-10-16 17:13:45

标签: react-router jestjs enzyme react-hooks react-router-dom

我正在尝试使用包裹在Router中的useEffect挂钩来测试功能组件。该组件是用TypeScript编写的,但不要分散注意力。

import React, { ReactNode, useEffect, useState, Fragment } from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { RouterProps } from 'component/hoc/router/Router';

export interface ClassIdChangedProps {
  children: ReactNode;
}

const ClassIdChanged = ({ 
  children,
  match: {
    params: { classId },
  },
}: ClassIdChangedProps & RouterProps) => {
  const [storedClassId, setStoredClassId] = useState(classId);

  useEffect(() => {
    if (classId !== storedClassId) {
      /** Test should hit this conditional by mocking 
       * router and grabbing a new classId param 
       **/
      alert('test should hit this');
      setStoredClassId(classId);
    }
  }, [ classId ]);

  return (
    <Fragment key={classId}>
      { children }
    </Fragment>
  )
};

export default withRouter<ClassIdChangedProps & RouteComponentProps<any>,
   typeof ClassIdChanged>(ClassIdChanged);

该测试应符合上述条件,因此需要更改模拟路由器和classId参数。

import React, { Fragment } from 'react';
import { MemoryRouter } from 'react-router-dom';
import { act } from '@testing-library/react';
import { createMemoryHistory } from 'history';
import { mount } from 'enzyme';
import ClassIdChanged from './ClassIdChanged';

describe('ClassIdChanged', () => {
  fit('should rerender children when classId changes on url parameters', () => {
    const history = createMemoryHistory();
    const spy = jest.fn();

    // failed attempt 1 - doesn't hit if block
    const wrapper = mount(
      <MemoryRouter initialEntries={['/class/1234']} >
        <ClassIdChanged>
          <Fragment>{spy()}</Fragment>
        </ClassIdChanged>
      </MemoryRouter>
    );

    // failed attempt 2 - doesn't hit if block
    const wrapper = mount(
      <ClassIdChanged>
        <Fragment>{spy()}</Fragment>
      </ClassIdChanged>, {
      wrappingComponent: MemoryRouter,
      childContextTypes: { match: { params: { classId: '1234' } } },
    });

    // failed attempt 3 - doesn't work, doesn't call useEffect, 
    const wrapper = mount(
      <Router>
        <ClassIdChanged 
          match={ { params: { classId: '1234' } } }
          >
          <div>{ spy() }</div>
        </ClassIdChanged>
      </Router>
    );

    // failed attempt 4 - doesn't work, doesn't call useEffect or hit if block
    const wrapper = mount(
      <MemoryRouter initialEntries={ ['/class/1234'] } >
        // @ts-ignore
        <ClassIdChanged 
          match={ { params: { classId: '1234' } } }
          >
          <div>{ spy() }</div>
        </ClassIdChanged>
      </MemoryRouter>
    );

    expect(spy).toHaveBeenCalledTimes(1);

    act(() => {
      history.push('/class/4321'); // has no effect on any of above route configurations
      wrapper.setProps( { match: { params: { classId: '4321' } } } );
      expect(spy).toHaveBeenCalledTimes(2);
    });

    wrapper.unmount();
  });
});

我尝试了各种模拟路由器的方法,但是尚不清楚如何在这些不同的尝试中更改路由。

问题归结为以下问题:

  1. 应使用哪种安装方法? -我假设安装在浅色或渲染上。
  2. 应该使用哪种方法来处理withRouter包装器?
  3. 在测试中应使用哪种方法来切换classId路由参数?

主要目标只是在组件代码中达到条件限制。

0 个答案:

没有答案