如何用 enzyme 测试 react-router

How to test react-router with enzyme

我正在使用 enzyme+mocha+chai 来测试我的 react-redux 项目。 Enzyme 提供浅表来测试组件行为。但是我没有找到测试路由器的方法。我正在使用 react-router 如下:

<Router history={browserHistory}>
     ...
        <Route path="nurse/authorization" component{NurseAuthorization}/>
     ...
  </Route>

我想测试这条路线 nurse/authorization 参考 NurseAuthorization 组件。如何在reactjs项目中测试?

EDIT1

我正在使用 react-router 作为路由器框架。

您可以将您的路由器包裹在一个组件中以便对其进行测试。

Routes.jsx

export default props => (
  <Router history={browserHistory}>
    ...
    <Route path="nurse/authorization" component{NurseAuthorization}/>
    ...
  </Route>
)

index.js

import Routes from './Routes.jsx';
...

ReactDOM.render(<Routes />, document.getElementById('root'));

然后你必须浅渲染你的Routes组件,你可以创建一个对象映射来检查路径和相关组件之间的对应关系。

Routes.test.js

import { shallow } from 'enzyme';
import { Route } from 'react-router';
import Routes from './Routes.jsx';
import NurseAuthorization from './NurseAuthorization.jsx';

it('renders correct routes', () => {
  const wrapper = shallow(<Routes />);
  const pathMap = wrapper.find(Route).reduce((pathMap, route) => {
    const routeProps = route.props();
    pathMap[routeProps.path] = routeProps.component;
    return pathMap;
  }, {});
  // { 'nurse/authorization' : NurseAuthorization, ... }

  expect(pathMap['nurse/authorization']).toBe(NurseAuthorization);
});

编辑

如果您想额外处理渲染道具的情况:

const pathMap = wrapper.find(Route).reduce((pathMap, route) => {
  const routeProps = route.props();
  if (routeProps.component) {
    pathMap[routeProps.path] = routeProps.component;
  } else if (routeProps.render) {
    pathMap[routeProps.path] = routeProps.render({}).type;
  }
  return pathMap;
}, {});

只有在您直接渲染要测试的组件(没有额外的包装器)的情况下它才会起作用。

<Route path="nurse/authorization" render{() => <NurseAuthorization />}/>

我在动态路由器的另一个文件中定义了我的路径,所以我也在测试我作为路由呈现的所有路由都在我的 paths.js 常量中定义:

it('Routes should only have paths declared in src/routing/paths.js', () => {
  const isDeclaredInPaths = (element, index, array) => {
    return pathsDefined.indexOf(array[index]) >= 0;
  }
  expect(routesDefined.every(isDeclaredInPaths)).to.be.true;
});

只有组件渲染成功才会通过: 它适用于 Redux 和 react-router,包括 hooks。

import React from "react";

import { expect } from "chai";
import { mount } from "enzyme";
import { MemoryRouter, Route } from "react-router-dom";
import { createMockStore } from "redux-test-utils";
import { Provider } from "react-redux";


...
describe("<MyComponent />", () => {
    it("renders the component", () => {
    let props = {
      index: 1,
      value: 1
    };
    let state = {};

    const wrapper = mount(
      <Provider store={createMockStore(state)}>
        <MemoryRouter initialEntries={["/s/parameter1"]}>
          <Route path="/s/:camera">
            <MyComponent {...props} />
          </Route>
        </MemoryRouter>
      </Provider>
    );

    expect(wrapper.find(ProcessedFrames.WrappedComponent)).to.have.lengthOf(1);
  });
});

已针对 react-router-dom v6

进行测试

根据@Freez 的回答,我实现了一个递归函数,即使您使用的是嵌套路由,也能returns 正确url 映射。

你只需要在 setupTests.js 中添加一次就可以在任何测试中使用它:

function recursiveGetPathMap(route, parentPath){
  let pathMap = {};
  const routeProps = route.props();
  let path = parentPath + (parentPath.length == 0 || parentPath[parentPath.length-1] == '/' ? '' : '/') + routeProps.path;
  pathMap[path] = routeProps.element.type;
  route.children(Route).forEach(el=>{
    pathMap = {...pathMap, ...recursiveGetPathMap(el, path)};
  });
  return pathMap;
}

global.getPathMap = (wrapper)=>{

  let pathMap = {};
  wrapper.find(Routes).children(Route).forEach(el =>{
    pathMap = {...pathMap, ...recursiveGetPathMap(el, "")};
  });

  return pathMap;
}

示例:

App.js

...
<Routes>
    <Route path="/" element={<Layout/>}>
        <Route path="users" element={<Users/>}>
            <Route path=":name" element={<Profile/>}/>
        </Route>
    </Route>
</Routes>
...

App.test.js

...
it('whatever', ()=>{
    const component = <App/>;
    const wrapper = shallow(component);
    const pathMap = getPathMap(wrapper);
    expect(pathMap['/']).toBe(Layout);
    expect(pathMap['/users']).toBe(Users);
    expect(pathMap['/users/:name']).toBe(Profile);
});
...

该示例中 console.log(pathMap) 的输出是:

{
    '/': [Function: Layout],
    '/users': [Function: Users],
    '/users/:name': [Function: Profile]
}

请注意,如果您有一条没有路径的路线(索引路线):

<Route index element={<SomeComponent/>}/>

路线会像/somepath/somepath/undefined