#reactjs #react-redux #react-router #react-testing-library
#reactjs #реагировать-redux #react-маршрутизатор #react-testing-library
Вопрос:
Я пытаюсь настроить тестовый файл для отображения маршрута / страницы в моем приложении. Я пытаюсь обернуть все с помощью Redux и маршрутизатора, и это то, что у меня есть:
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. Сбой в конструкторе компонента при инициализации состояния:
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, чтобы он мог получить эти параметры маршрутизатора?
Комментарии:
1. @flppv, можете ли вы создать минимальный репозиторий git для решения проблемы?
2. @TarunLalwani вопрос действительно не о минимальной настройке, я понятия не имею, как написать тест для компонента, который зависит от множества вещей вокруг него — Redux, Sagas, Router и т.д.
Ответ №1:
Вы разместили свой <ViewNode />
компонент внутри Route
, но забыли передать полученные им реквизиты. Вот почему props.match
не определено в вашем компоненте.
Вы можете сделать это вместо:
<Route path="/server/:env/:nodeName">
{props => <ViewNode {...props} />}
</Route>
В принципе, вы можете использовать один из 3 способов визуализации чего-либо с помощью <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')
})
Комментарии:
1. @flppv Я надеюсь, что этот пример поможет вам. Вы также можете взглянуть на некоторые примеры из Kent
2. спасибо, я видел все примеры в репозитории и по всему Интернету. Я никогда не видел, чтобы тестировались действительно сложные компоненты, но теперь у меня есть несколько примеров, я думаю, я должен делать один и тот же большой шаблон в каждом тесте.
Ответ №2:
Вот как я тестировал свои маршруты.
- Вы используете react-redux для поставщика
- Вы создаете начальное состояние для своего хранилища
- добавьте его к своему провайдеру
- теперь вы можете выбирать элементы, ожидая, что они будут соответствовать вашему 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>');
});
});
Комментарии:
1. Итак, в каждом тесте, если мой компонент сложный, мне приходится каждый раз перестраивать большое количество оберток вокруг него, это то, что я хотел знать, спасибо
2. Нет необходимости создавать весь этот код для каждого теста. Просто возьмите компонент, который вы хотите протестировать, с помощью route path или любым другим способом, которым вы это делаете. Создайте вспомогательную функцию, которая создаст вышеупомянутое хранилище redux и маршрутизатор, который просто введет в него ваш протестированный компонент. Что-то вроде: renderTestEnv (состояние, реквизиты, TesteComponent)