В тестировании React-приложений особую важность приобретает проверка
логики хуков. Стандартные подходы с рендерингом компонентов не всегда
удобны, особенно когда требуется тестировать пользовательские хуки
отдельно от UI. Для этого используется функция renderHook
из библиотеки @testing-library/react-hooks,
которая позволяет изолированно вызывать и проверять хуки.
renderHookrenderHook принимает на вход функцию, возвращающую хук.
Эта функция выполняется так, как если бы хук был вызван внутри
компонента. Возвращаемое значение функции renderHook —
объект с ключевыми свойствами для взаимодействия с хуком:
result — объект, содержащий текущее состояние хука в
result.current.rerender — функция для повторного рендеринга с новыми
аргументами.unmount — функция для размонтирования хука.waitForNextUpdate — промис, который ожидает следующего
обновления состояния.Пример базового использования:
import { renderHook, act } from '@testing-library/react-hooks';
import { useState } from 'react';
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(c => c + 1);
return { count, increment };
}
const { result } = renderHook(() => useCounter(5));
console.log(result.current.count); // 5
act(() => {
result.current.increment();
});
console.log(result.current.count); // 6
Ключевой момент: любые действия, изменяющие состояние хука, должны
быть обёрнуты в act, чтобы корректно тестировать
асинхронные обновления и избежать предупреждений React.
Многие хуки принимают аргументы. renderHook позволяет
передавать их через функцию:
const { result, rerender } = renderHook(
({ initial }) => useCounter(initial),
{ initialProps: { initial: 10 } }
);
console.log(result.current.count); // 10
rerender({ initial: 20 });
console.log(result.current.count); // 20
initialProps задаёт начальные аргументы.rerender(newProps) обновляет хук с новыми параметрами,
что важно для тестирования динамического поведения.Для хуков, которые используют useEffect с асинхронными
операциями, применяются методы waitFor и
waitForNextUpdate. Они позволяют дождаться завершения
обновления состояния перед проверкой:
import { useEffect, useState } from 'react';
function useAsyncValue(fetcher) {
const [value, setValue] = useState(null);
useEffect(() => {
let mounted = true;
fetcher().then(result => {
if (mounted) setValue(result);
});
return () => {
mounted = false;
};
}, [fetcher]);
return value;
}
const fetcher = () => Promise.resolve(42);
const { result, waitForNextUpdate } = renderHook(() => useAsyncValue(fetcher));
await waitForNextUpdate();
console.log(result.current); // 42
Методы ожидания позволяют корректно тестировать хуки с асинхронными побочными эффектами, избегая гонок и состояния «undefined» при проверке результата.
Некоторые хуки зависят от контекста React. renderHook
предоставляет опцию wrapper, которая позволяет обернуть хук
в необходимые провайдеры:
import { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
function useTheme() {
return useContext(ThemeContext);
}
const wrapper = ({ children }) => (
<ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>
);
const { result } = renderHook(() => useTheme(), { wrapper });
console.log(result.current); // "dark"
wrapper — компонент, оборачивающий хук, что позволяет
подставлять контексты, роутеры, Redux-провайдеры и другие обёртки.renderHook совместно с act обеспечивает
безопасное тестирование любых изменений состояния:
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
Важно: каждое обновление состояния, связанное с побочными эффектами
или событиями, должно быть внутри act. Это синхронизирует
состояние с механизмом рендера React.
renderHook для сложных хуковrenderHook позволяет протестировать конечный
результат без необходимости рендерить компонент.unmount можно проверить, что все побочные эффекты корректно
очистились.rerender легко проверять, что значения изменяются только
при нужных условиях.act для всех изменений состояния.waitForNextUpdate
или waitFor.wrapper для хуков, зависящих от
контекста.unmount
при тестировании побочных эффектов.renderHook является мощным инструментом для
unit-тестирования хуков в изоляции, позволяя быстро выявлять ошибки
логики и асинхронных операций без необходимости рендеринга сложных
компонентов.