如何用 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
我正在使用 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