Обёрнутые компоненты с Context

Enzyme предоставляет мощные инструменты для тестирования компонентов в React, включая поддержку различных уровней рендеринга и тестирования компонентов, использующих контексты. В этой главе рассмотрим, как правильно тестировать компоненты, которые обёрнуты в Context.Provider, а также как эффективно работать с состоянием контекста внутри тестов.

Что такое Context в React?

Context в React используется для передачи данных между компонентами на разных уровнях дерева без необходимости прокидывать их через пропсы. Это особенно полезно для глобальных состояний, таких как темы, языковые настройки или текущий пользователь. Контекст создаётся с помощью функции React.createContext(), которая возвращает объект с двумя основными компонентами: Provider и Consumer.

const MyContext = React.createContext();

MyContext.Provider передаёт значение всему дереву компонентов, которые оборачиваются в него. Компоненты, использующие MyContext.Consumer, могут получать это значение. Когда необходимо протестировать компоненты, использующие контекст, важно правильно настроить среду тестирования, чтобы обеспечить правильную работу контекста.

Тестирование компонентов с контекстом с помощью Enzyme

Enzyme предоставляет несколько методов рендеринга, таких как shallow, mount, и render. Для тестирования компонентов, которые зависят от контекста, чаще всего используется метод mount, так как он позволяет рендерить компонент и все его дочерние компоненты с полной иерархией React, включая контекст.

Пример компонента, использующего Context

Рассмотрим компонент, который использует контекст для отображения информации о пользователе.

const UserContext = React.createContext();

const UserProfile = () => {
  const user = React.useContext(UserContext);
  return <div>{user.name}</div>;
};

В данном случае компонент UserProfile получает информацию о пользователе через контекст UserContext.

Тестирование с mount и контекстом

Чтобы протестировать компонент, который использует контекст, необходимо обернуть его в Provider, передав нужное значение контекста. Для этого можно воспользоваться методом mount из Enzyme.

import { mount } from 'enzyme';
import { UserProfile, UserContext } from './UserProfile';

describe('UserProfile', () => {
  it('должен отображать имя пользователя из контекста', () => {
    const user = { name: 'Иван' };
    
    const wrapper = mount(
      <UserContext.Provider value={user}>
        <UserProfile />
      </UserContext.Provider>
    );
    
    expect(wrapper.text()).toBe('Иван');
  });
});

В этом тесте компонент UserProfile обёрнут в UserContext.Provider, и тест проверяет, что имя пользователя отображается правильно. Такой подход позволяет тестировать компоненты, которые зависят от контекста, в изолированном окружении.

Передача значений через контекст в тестах

Для более сложных случаев, когда контекст может изменяться или быть динамичным, можно передавать разные значения контекста для тестирования разных сценариев. В таких случаях стоит использовать дополнительные обёртки или создавать мок-значения для контекста.

describe('UserProfile', () => {
  it('должен отображать правильное имя пользователя', () => {
    const user = { name: 'Мария' };
    
    const wrapper = mount(
      <UserContext.Provider value={user}>
        <UserProfile />
      </UserContext.Provider>
    );
    
    expect(wrapper.text()).toBe('Мария');
  });

  it('должен отображать другое имя пользователя', () => {
    const user = { name: 'Алексей' };
    
    const wrapper = mount(
      <UserContext.Provider value={user}>
        <UserProfile />
      </UserContext.Provider>
    );
    
    expect(wrapper.text()).toBe('Алексей');
  });
});

Этот пример демонстрирует тестирование нескольких сценариев с разными значениями контекста, что позволяет убедиться, что компонент правильно обрабатывает изменения в контексте.

Мокирование значений контекста

Если необходимо протестировать компонент с определённым значением контекста, можно использовать мок-данные, чтобы замаскировать реальный контекст. Это особенно полезно, когда контекст является частью более сложной логики, например, для имитации ошибок или особых состояний.

const mockUserContext = { name: 'Петр' };

const wrapper = mount(
  <UserContext.Provider value={mockUserContext}>
    <UserProfile />
  </UserContext.Provider>
);

expect(wrapper.text()).toBe('Петр');

Мокирование контекста позволяет избежать зависимости от реальных данных и тестировать компонент в изолированной среде.

Тестирование взаимодействия с контекстом

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

it('должен обновлять отображаемое имя при изменении контекста', () => {
  const user = { name: 'Елена' };
  const wrapper = mount(
    <UserContext.Provider value={user}>
      <UserProfile />
    </UserContext.Provider>
  );

  expect(wrapper.text()).toBe('Елена');

  const newUser = { name: 'Сергей' };
  wrapper.setProps({
    value: newUser,
  });

  wrapper.update();
  expect(wrapper.text()).toBe('Сергей');
});

Этот пример показывает, как можно обновить контекст с помощью setProps() и проверить, что компонент правильно реагирует на изменения данных. Это полезно, когда компонент зависит от динамично изменяющихся значений контекста.

Использование Context вместе с хуками

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

const ThemeContext = React.createContext('light');

const ThemedComponent = () => {
  const theme = React.useContext(ThemeContext);
  return <div className={theme}>Тема: {theme}</div>;
};

describe('ThemedComponent', () => {
  it('должен использовать контекст для установки темы', () => {
    const wrapper = mount(
      <ThemeContext.Provider value="dark">
        <ThemedComponent />
      </ThemeContext.Provider>
    );

    expect(wrapper.find('div').hasClass('dark')).toBe(true);
    expect(wrapper.text()).toBe('Тема: dark');
  });
});

В данном случае компонент ThemedComponent использует хук useContext, чтобы получить текущую тему. В тесте проверяется, что компонент правильно рендерит данные на основе контекста.

Резюме

Тестирование компонентов, использующих Context, в Enzyme требует правильной настройки обёртки компонента в Provider. Методы рендеринга, такие как mount, позволяют полностью проверить взаимодействие компонента с контекстом, включая передачу значений, их изменение и реакцию компонента на обновления контекста. Мокирование контекста и использование динамичных значений позволяют гибко тестировать различные сценарии, обеспечивая полноту тестов и уверенность в корректной работе приложения.