React-Testing-Library - 使用 Redux 和路由器包装组件
React-Testing-Library - Wrapping Component with Redux and Router
我正在尝试设置一个测试文件以在我的应用程序上呈现 route/page。我正在尝试用 Redux 和 Router 包装所有东西,这就是我所拥有的:
import React from 'react';
import { render } from 'react-testing-library';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from '../../store/reducer';
import {Link, Route, Router, Switch} from 'react-router-dom'
import {createMemoryHistory} from 'history'
import ViewNode from '../Pages/ViewNode';
const customRender = (
ui,
{
route = '/',
history = createMemoryHistory({ initialEntries: [route] }),
initialState,
store = createStore(reducer, initialState),
...options
} = {}
) => ({
...render(
<Provider store={store}>
<Router history={history}>{ui}</Router>
</Provider>,
options
),
history,
});
test('can render with redux and router', () => {
const { getByTestId } = customRender(
<Route path="/server/:env/:nodeName">
<ViewNode />
</Route>,
{
route: '/server/prod/some.server.name.com',
}
);
expect(getByTestId('page-content')).toBeVisible()
})
然后我得到以下错误:
Error: Uncaught [TypeError: Cannot read property 'params' of undefined]
抛出错误的原因是它找不到 React Router 参数。当我初始化状态时,它在组件构造函数中失败:
this.state = {
modal: false,
activeTab: '1',
imageStatus: "loading",
env: props.match.params.env, //failing here
nodeName: props.match.params.nodeName,
environments: props.environments,
}
我上面的实现似乎没有正确包装路由器。
我如何使用 Redux 和 Router 正确包装我的页面组件,以便它可以获得这些路由器参数?
这就是我测试路线的方式。
- 你为 Provider 使用了 react-redux
- 您为商店创建初始状态
- 将其添加到您的提供商
- 现在您可以 select 个元素,期望它们与您的 html(每个示例)相匹配
import { render } from '@testing-library/react';
import { Router, Switch, Route } from 'react-router-dom';
import { createMemoryHistory } from 'history';
import { Provider } from 'react-redux';
import React from 'react';
import createStore from 'redux-mock-store';
jest.mock('../../components/Form/ManagerSelect', () => jest.fn(() => null));
describe('router page', () => {
const createState = state => {
return {
//whatever u need
}
};
const Home = _ => <span>home</span>;
const Profile = _ => <span>profile</span>;
const renderComponent = state => {
const store = createStore()(state);
//this is the "history" of your app like:
// homepage -> about -> contact -> cart page ...
const initialEntries = ['/'];
return render(
<Provider store={store}>
<Router history={createMemoryHistory({ initialEntries })}>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/profile" component={Profile} />
</Switch>
</Router>
</Provider>
);
};
it('missing emergency details should redirect to profile', () => {
const rendered = renderComponent(createState());
expect(rendered.container.innerHTML).toEqual('<span>profile</span>');
});
});
您已将 <ViewNode />
组件放置在 Route
中,但忘记传递它收到的道具。这就是为什么 props.match
在您的组件中未定义。
您可以这样做:
<Route path="/server/:env/:nodeName">
{props => <ViewNode {...props} />}
</Route>
基本上可以用one of the 3 ways to render something with a <Route>
.
这是一个工作示例:
import React from 'react'
import {Route, Router} from 'react-router-dom'
import {createMemoryHistory} from 'history'
import {render, fireEvent} from '@testing-library/react'
import {createStore} from 'redux'
import {Provider, connect} from 'react-redux'
function reducer(state = {count: 0}, action) {
switch (action.type) {
case 'INCREMENT':
return {
count: state.count + 1,
}
case 'DECREMENT':
return {
count: state.count - 1,
}
default:
return state
}
}
class Counter extends React.Component {
increment = () => {
this.props.dispatch({type: 'INCREMENT'})
}
decrement = () => {
this.props.dispatch({type: 'DECREMENT'})
}
render() {
return (
<div>
<div data-testid="env-display">{this.props.match.params.env}</div>
<div data-testid="location-display">{this.props.location.pathname}</div>
<div>
<button onClick={this.decrement}>-</button>
<span data-testid="count-value">{this.props.count}</span>
<button onClick={this.increment}>+</button>
</div>
</div>
)
}
}
const ConnectedCounter = connect(state => ({count: state.count}))(Counter)
function customRender(
ui,
{
initialState,
store = createStore(reducer, initialState),
route = '/',
history = createMemoryHistory({initialEntries: [route]}),
} = {},
) {
return {
...render(
<Provider store={store}>
<Router history={history}>{ui}</Router>
</Provider>,
),
store,
history,
}
}
test('can render with redux and router', () => {
const {getByTestId, getByText} = customRender(
<Route path="/server/:env/:nodeName">
{props => <ConnectedCounter {...props} />}
</Route>,
{
route: '/server/prod/some.server.name.com',
},
)
expect(getByTestId('env-display')).toHaveTextContent('prod')
expect(getByTestId('location-display')).toHaveTextContent(
'/server/prod/some.server.name.com',
)
fireEvent.click(getByText('+'))
expect(getByTestId('count-value')).toHaveTextContent('1')
})
我正在尝试设置一个测试文件以在我的应用程序上呈现 route/page。我正在尝试用 Redux 和 Router 包装所有东西,这就是我所拥有的:
import React from 'react';
import { render } from 'react-testing-library';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from '../../store/reducer';
import {Link, Route, Router, Switch} from 'react-router-dom'
import {createMemoryHistory} from 'history'
import ViewNode from '../Pages/ViewNode';
const customRender = (
ui,
{
route = '/',
history = createMemoryHistory({ initialEntries: [route] }),
initialState,
store = createStore(reducer, initialState),
...options
} = {}
) => ({
...render(
<Provider store={store}>
<Router history={history}>{ui}</Router>
</Provider>,
options
),
history,
});
test('can render with redux and router', () => {
const { getByTestId } = customRender(
<Route path="/server/:env/:nodeName">
<ViewNode />
</Route>,
{
route: '/server/prod/some.server.name.com',
}
);
expect(getByTestId('page-content')).toBeVisible()
})
然后我得到以下错误:
Error: Uncaught [TypeError: Cannot read property 'params' of undefined]
抛出错误的原因是它找不到 React Router 参数。当我初始化状态时,它在组件构造函数中失败:
this.state = {
modal: false,
activeTab: '1',
imageStatus: "loading",
env: props.match.params.env, //failing here
nodeName: props.match.params.nodeName,
environments: props.environments,
}
我上面的实现似乎没有正确包装路由器。
我如何使用 Redux 和 Router 正确包装我的页面组件,以便它可以获得这些路由器参数?
这就是我测试路线的方式。
- 你为 Provider 使用了 react-redux
- 您为商店创建初始状态
- 将其添加到您的提供商
- 现在您可以 select 个元素,期望它们与您的 html(每个示例)相匹配
import { render } from '@testing-library/react';
import { Router, Switch, Route } from 'react-router-dom';
import { createMemoryHistory } from 'history';
import { Provider } from 'react-redux';
import React from 'react';
import createStore from 'redux-mock-store';
jest.mock('../../components/Form/ManagerSelect', () => jest.fn(() => null));
describe('router page', () => {
const createState = state => {
return {
//whatever u need
}
};
const Home = _ => <span>home</span>;
const Profile = _ => <span>profile</span>;
const renderComponent = state => {
const store = createStore()(state);
//this is the "history" of your app like:
// homepage -> about -> contact -> cart page ...
const initialEntries = ['/'];
return render(
<Provider store={store}>
<Router history={createMemoryHistory({ initialEntries })}>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/profile" component={Profile} />
</Switch>
</Router>
</Provider>
);
};
it('missing emergency details should redirect to profile', () => {
const rendered = renderComponent(createState());
expect(rendered.container.innerHTML).toEqual('<span>profile</span>');
});
});
您已将 <ViewNode />
组件放置在 Route
中,但忘记传递它收到的道具。这就是为什么 props.match
在您的组件中未定义。
您可以这样做:
<Route path="/server/:env/:nodeName">
{props => <ViewNode {...props} />}
</Route>
基本上可以用one of the 3 ways to render something with a <Route>
.
这是一个工作示例:
import React from 'react'
import {Route, Router} from 'react-router-dom'
import {createMemoryHistory} from 'history'
import {render, fireEvent} from '@testing-library/react'
import {createStore} from 'redux'
import {Provider, connect} from 'react-redux'
function reducer(state = {count: 0}, action) {
switch (action.type) {
case 'INCREMENT':
return {
count: state.count + 1,
}
case 'DECREMENT':
return {
count: state.count - 1,
}
default:
return state
}
}
class Counter extends React.Component {
increment = () => {
this.props.dispatch({type: 'INCREMENT'})
}
decrement = () => {
this.props.dispatch({type: 'DECREMENT'})
}
render() {
return (
<div>
<div data-testid="env-display">{this.props.match.params.env}</div>
<div data-testid="location-display">{this.props.location.pathname}</div>
<div>
<button onClick={this.decrement}>-</button>
<span data-testid="count-value">{this.props.count}</span>
<button onClick={this.increment}>+</button>
</div>
</div>
)
}
}
const ConnectedCounter = connect(state => ({count: state.count}))(Counter)
function customRender(
ui,
{
initialState,
store = createStore(reducer, initialState),
route = '/',
history = createMemoryHistory({initialEntries: [route]}),
} = {},
) {
return {
...render(
<Provider store={store}>
<Router history={history}>{ui}</Router>
</Provider>,
),
store,
history,
}
}
test('can render with redux and router', () => {
const {getByTestId, getByText} = customRender(
<Route path="/server/:env/:nodeName">
{props => <ConnectedCounter {...props} />}
</Route>,
{
route: '/server/prod/some.server.name.com',
},
)
expect(getByTestId('env-display')).toHaveTextContent('prod')
expect(getByTestId('location-display')).toHaveTextContent(
'/server/prod/some.server.name.com',
)
fireEvent.click(getByText('+'))
expect(getByTestId('count-value')).toHaveTextContent('1')
})