Тестирование custom хуков

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

Что такое custom хуки?

Custom хуки представляют собой функции в React, которые используют встроенные хуки, такие как useState, useEffect, и другие. Они позволяют инкапсулировать повторяющийся функционал и повторно использовать его в различных компонентах. Хуки могут хранить локальное состояние, управлять побочными эффектами и взаимодействовать с API.

Пример простого custom хука:

import { useState } from 'react';

export function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue);

  const increment = () => setCount(count + 1);
  const decrement = () => setCount(count - 1);

  return { count, increment, decrement };
}

Этот хук инкапсулирует логику счётчика и может быть использован в различных компонентах.

Проблемы, с которыми сталкиваются при тестировании custom хуков

При тестировании custom хуков можно столкнуться с несколькими проблемами:

  1. Доступ к хук-логике: хуки не могут быть использованы вне компонентов или тестов, использующих специальные обёртки.
  2. Сложности с мокающими функциями: хуки могут зависеть от контекста или других функций, что затрудняет их тестирование без имитации поведения.
  3. Побочные эффекты: хуки, использующие такие методы как useEffect, могут выполнять асинхронные операции, которые требуют правильной синхронизации с тестами.

Enzyme предоставляет определённые подходы для работы с хуками и компонентов в тестах, но для полноценного тестирования custom хуков лучше использовать вспомогательные инструменты, такие как React Testing Library или mock-функции.

Использование Enzyme для тестирования хуков

Для тестирования хуков с помощью Enzyme существует несколько подходов. Прежде чем приступить к самому тестированию, необходимо установить необходимые пакеты:

npm install --save-dev enzyme enzyme-adapter-react-16 jest

Кроме того, потребуется настроить адаптер для Enzyme, чтобы он правильно работал с React. Например:

import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

configure({ adapter: new Adapter() });

После настройки можно приступать к тестированию хуков.

Тестирование хуков с помощью обёртки

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

Пример тестирования хука useCounter:

import { shallow } from 'enzyme';
import { useCounter } from './useCounter';

function TestComponent() {
  const { count, increment, decrement } = useCounter(0);
  return (
    <div>
      <span>{count}</span>
      <button onCl ick={increment}>Increment</button>
      <button onCl ick={decrement}>Decrement</button>
    </div>
  );
}

describe('useCounter hook', () => {
  it('should initialize count to the passed initial value', () => {
    const wrapper = shallow(<TestComponent />);
    expect(wrapper.find('span').text()).toBe('0');
  });

  it('should increment the count when the increment button is clicked', () => {
    const wrapper = shallow(<TestComponent />);
    wrapper.find('button').at(0).simulate('click');
    expect(wrapper.find('span').text()).toBe('1');
  });

  it('should decrement the count when the decrement button is clicked', () => {
    const wrapper = shallow(<TestComponent />);
    wrapper.find('button').at(1).simulate('click');
    expect(wrapper.find('span').text()).toBe('-1');
  });
});

Здесь создаётся компонент TestComponent, который использует хук useCounter. В тестах проверяется начальное значение счётчика, а также реакция на действия пользователя, такие как нажатие кнопок для инкремента и декремента.

Мокающие хуки с использованием useState и useEffect

Часто хуки взаимодействуют с внешними данными или выполняют асинхронные операции через useEffect. В таких случаях может понадобиться мокировать эти функции, чтобы проверить, как хук ведёт себя при изменении состояния или выполнении побочных эффектов.

Пример использования мока для асинхронного хука с useEffect:

import { useEffect, useState } from 'react';

export function useFetchData(url) {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err);
      }
    };
    fetchData();
  }, [url]);

  return { data, error };
}

Для тестирования этого хука можно использовать моки:

import { shallow } from 'enzyme';
import { useFetchData } from './useFetchData';

jest.mock('fetch', () => jest.fn());

describe('useFetchData hook', () => {
  it('should fetch data successfully', async () => {
    fetch.mockResolvedValueOnce({
      json: () => Promise.resolve({ message: 'Success' }),
    });

    const wrapper = shallow(<TestComponent />); // где TestComponent использует useFetchData
    await wrapper.instance().componentDidMount();
    expect(wrapper.state().data).toEqual({ message: 'Success' });
  });

  it('should handle errors during fetch', async () => {
    fetch.mockRejectedValueOnce(new Error('Failed to fetch'));

    const wrapper = shallow(<TestComponent />);
    await wrapper.instance().componentDidMount();
    expect(wrapper.state().error).toEqual(new Error('Failed to fetch'));
  });
});

В этом примере хук useFetchData мокирует функцию fetch, которая обычно делает HTTP-запрос. Это позволяет тестировать логику хука, не совершая реальных сетевых запросов. В тестах проверяются успешные запросы и обработка ошибок.

Использование хуков в тестах с React Testing Library

Enzyme не всегда является идеальным выбором для тестирования хуков, особенно когда речь идёт о более сложных сценариях. В таких случаях React Testing Library предоставляет более естественный способ работы с хуками и компонентами. Хотя Enzyme хорошо подходит для изолированного тестирования хуков через обёртки, React Testing Library часто оказывается более гибким и позволяет тестировать компоненты с хуками в реальных условиях их использования.

Тем не менее, Enzyme остаётся полезным инструментом для более прямого тестирования взаимодействий внутри компонентов.