Тестирование маршрутизированных компонентов

Маршрутизированные компоненты, используемые в современных одностраничных приложениях (SPA), управляются с помощью таких библиотек, как React Router. Эти компоненты часто содержат логику, зависимую от маршрута, параметров URL и состояния навигации. Тестирование таких компонентов с помощью Enzyme представляет собой сложную задачу, так как необходимо симулировать поведение маршрутизатора и взаимодействие с компонентами в зависимости от изменений маршрута.

Основы тестирования с Enzyme и React Router

Для того чтобы тестировать компоненты, использующие React Router, необходимо правильно настроить среду тестирования. Enzyme сам по себе не предоставляет функционала для работы с маршрутизатором, поэтому следует использовать библиотеку MemoryRouter из React Router для имитации поведения маршрутизатора. MemoryRouter позволяет эмулировать изменения маршрута без необходимости использования реального браузера.

Пример базовой настройки для теста маршрутизированного компонента:

import { shallow } from 'enzyme';
import { MemoryRouter } from 'react-router-dom';
import MyComponent from './MyComponent';

describe('MyComponent', () => {
  it('должен рендериться с правильным маршрутом', () => {
    const wrapper = shallow(
      <MemoryRouter initialEntries={['/home']}>
        <MyComponent />
      </MemoryRouter>
    );
    expect(wrapper.find('h1').text()).toEqual('Home Page');
  });
});

В данном примере используется MemoryRouter с параметром initialEntries, который определяет начальный маршрут. Таким образом, можно симулировать состояние маршрутизатора в тестах.

Тестирование переходов между маршрутами

Один из ключевых аспектов тестирования маршрутизированных компонентов — это проверка поведения при изменении маршрута. С помощью Enzyme можно тестировать, как компоненты реагируют на изменения маршрута, обновляя своё содержимое или выполняя другие действия.

Для тестирования маршрутов можно использовать метод mount вместо shallow. Это позволяет воссоздать более полный контекст, включая рендеринг дочерних компонентов.

Пример теста для проверки перехода между маршрутами:

import { mount } from 'enzyme';
import { MemoryRouter, Route, Switch } from 'react-router-dom';
import MyComponent from './MyComponent';
import HomePage from './HomePage';
import AboutPage from './AboutPage';

describe('Роутинг с MemoryRouter', () => {
  it('должен отображать компонент в зависимости от маршрута', () => {
    const wrapper = mount(
      <MemoryRouter initialEntries={['/home']}>
        <Switch>
          <Route path="/home" component={HomePage} />
          <Route path="/about" component={AboutPage} />
        </Switch>
      </MemoryRouter>
    );

    expect(wrapper.find(HomePage)).toHaveLength(1);
    expect(wrapper.find(AboutPage)).toHaveLength(0);

    wrapper.setProps({ initialEntries: ['/about'] });

    expect(wrapper.find(HomePage)).toHaveLength(0);
    expect(wrapper.find(AboutPage)).toHaveLength(1);
  });
});

В этом примере тестируется рендеринг компонентов в зависимости от маршрута. При изменении маршрута с /home на /about компонент должен обновиться, что и проверяется с помощью setProps.

Тестирование компонентов с параметрами маршрута

Многие компоненты зависят от параметров маршрута, таких как идентификаторы в URL или query-параметры. При тестировании таких компонентов важно эмулировать маршруты с параметрами.

Пример теста для компонента, который использует параметры маршрута:

import { mount } from 'enzyme';
import { MemoryRouter, Route } from 'react-router-dom';
import UserProfile from './UserProfile';

describe('UserProfile', () => {
  it('должен отображать информацию о пользователе по id', () => {
    const wrapper = mount(
      <MemoryRouter initialEntries={['/user/123']}>
        <Route path="/user/:id" render={({ match }) => <UserProfile userId={match.params.id} />} />
      </MemoryRouter>
    );

    expect(wrapper.find(UserProfile).prop('userId')).toEqual('123');
  });
});

В этом примере используется динамический параметр маршрута :id, который передается в компонент через пропс match.params.id. Тест проверяет, что компонент получает правильный параметр маршрута и отображает данные, соответствующие этому параметру.

Тестирование навигации и методов маршрутизатора

Для проверки навигации и действий, связанных с изменением маршрута, можно использовать методы маршрутизатора, такие как history.push() или history.replace(). Enzyme позволяет имитировать вызовы этих методов для проверки поведения компонента при изменении маршрута.

Пример теста для проверки вызова history.push():

import { mount } from 'enzyme';
import { MemoryRouter, Route, useHistory } from 'react-router-dom';
import NavigationButton from './NavigationButton';

describe('NavigationButton', () => {
  it('должен перенаправлять на новый маршрут при клике', () => {
    const wrapper = mount(
      <MemoryRouter initialEntries={['/']}>
        <Route path="/" exact render={() => <NavigationButton />} />
        <Route path="/next" render={() => <div>Next Page</div>} />
      </MemoryRouter>
    );

    wrapper.find(NavigationButton).simulate('click');
    expect(wrapper.find('div').text()).toEqual('Next Page');
  });
});

В этом примере компонент NavigationButton вызывает метод history.push() при клике, что изменяет маршрут. Тест проверяет, что после клика рендерится новый компонент, соответствующий маршруту /next.

Тестирование защищенных маршрутов

В реальных приложениях часто встречаются защищенные маршруты, доступ к которым ограничен правами пользователя или состоянием аутентификации. При тестировании таких маршрутов необходимо проверить, что компоненты корректно отображаются в зависимости от состояния аутентификации.

Пример теста для защищенного маршрута:

import { mount } from 'enzyme';
import { MemoryRouter, Route, Redirect } from 'react-router-dom';
import ProtectedRoute from './ProtectedRoute';

describe('ProtectedRoute', () => {
  it('должен перенаправлять на страницу логина, если пользователь не авторизован', () => {
    const wrapper = mount(
      <MemoryRouter initialEntries={['/protected']}>
        <Route path="/protected" render={() => <ProtectedRoute isAuthenticated={false} />} />
        <Route path="/login" render={() => <div>Login Page</div>} />
      </MemoryRouter>
    );

    expect(wrapper.find('div').text()).toEqual('Login Page');
  });

  it('должен отображать защищенный компонент, если пользователь авторизован', () => {
    const wrapper = mount(
      <MemoryRouter initialEntries={['/protected']}>
        <Route path="/protected" render={() => <ProtectedRoute isAuthenticated={true} />} />
      </MemoryRouter>
    );

    expect(wrapper.find(ProtectedRoute)).toHaveLength(1);
  });
});

Этот пример показывает, как тестировать защищенные маршруты с проверкой состояния аутентификации. Если пользователь не авторизован, происходит редирект на страницу входа.

Заключение

Тестирование маршрутизированных компонентов требует внимательного подхода к настройке маршрутизатора, обработке маршрутов с параметрами и тестированию навигации. Использование Enzyme вместе с React Router позволяет создавать тесты, которые точно отражают поведение компонентов в реальных условиях.